Java Language
Pools d'executor, d'executorService et de threads
Recherche…
Introduction
L'interface Executor de Java offre un moyen de découpler la soumission des tâches des mécanismes d'exécution de chaque tâche, y compris des détails sur l'utilisation des threads, la planification, etc. Un exécuteur est normalement utilisé au lieu de créer explicitement des threads. Avec les exécuteurs, les développeurs n'auront pas à réécrire leur code de manière significative pour pouvoir ajuster facilement la stratégie d'exécution des tâches de leur programme.
Remarques
Pièges
- Lorsque vous planifiez une tâche pour une exécution répétée, en fonction du ScheduledExecutorService utilisé, votre tâche peut être suspendue de toute exécution ultérieure, si une exécution de votre tâche provoque une exception qui n'est pas gérée. Voir Mère F ** k le ScheduledExecutorService!
Fire and Forget - Tâches exécutables
Les exécuteurs acceptent un java.lang.Runnable
qui contient du code (potentiellement computationnel ou autre long ou lourd) à exécuter dans un autre thread.
L'utilisation serait:
Executor exec = anExecutor;
exec.execute(new Runnable() {
@Override public void run() {
//offloaded work, no need to get result back
}
});
Notez qu'avec cet exécuteur, vous n'avez aucun moyen de récupérer une valeur calculée.
Avec Java 8, on peut utiliser lambdas pour raccourcir l’exemple de code.
Executor exec = anExecutor;
exec.execute(() -> {
//offloaded work, no need to get result back
});
ThreadPoolExecutor
Un exécuteur commun utilisé est le ThreadPoolExecutor
, qui prend en charge la gestion des threads. Vous pouvez configurer la quantité minimale de threads que l'exécuteur doit toujours maintenir lorsqu'il n'y a pas grand-chose à faire (la taille de base) et une taille de thread maximale à laquelle le pool peut évoluer, s'il reste du travail à faire. Une fois que la charge de travail diminue, le pool réduit à nouveau le nombre de threads jusqu'à ce qu'il atteigne la taille minimale.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, // keep at least one thread ready,
// even if no Runnables are executed
5, // at most five Runnables/Threads
// executed in parallel
1, TimeUnit.MINUTES, // idle Threads terminated after one
// minute, when min Pool size exceeded
new ArrayBlockingQueue<Runnable>(10)); // outstanding Runnables are kept here
pool.execute(new Runnable() {
@Override public void run() {
//code to run
}
});
Remarque Si vous configurez ThreadPoolExecutor
avec une file d'attente illimitée , le nombre de threads ne dépassera pas corePoolSize
car les nouveaux threads ne sont créés que si la file d'attente est pleine:
ThreadPoolExecutor avec tous les paramètres:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
de JavaDoc
S'il y a plus de corePoolSize mais moins de threads maximumPoolSize, un nouveau thread sera créé uniquement si la file d'attente est pleine.
Avantages:
La taille de BlockingQueue peut être contrôlée et des scénarios de mémoire insuffisante peuvent être évités. Les performances des applications ne seront pas dégradées avec une taille de file d'attente limitée.
Vous pouvez utiliser des stratégies existantes ou créer de nouvelles stratégies de rejet.
Dans le ThreadPoolExecutor.AbortPolicy par défaut, le gestionnaire renvoie une exception RejectedExecutionException à l'exécution lors du rejet.
Dans
ThreadPoolExecutor.CallerRunsPolicy
, le thread qui appelle exécute lui-même exécute la tâche. Cela fournit un mécanisme de contrôle de rétroaction simple qui ralentira le taux de soumission de nouvelles tâches.Dans
ThreadPoolExecutor.DiscardPolicy
, une tâche qui ne peut pas être exécutée est simplement supprimée.Dans
ThreadPoolExecutor.DiscardOldestPolicy
, si l'exécuteur n'est pas arrêté, la tâche en tête de la file d'attente de travail est supprimée, puis l'exécution est exécutée à nouveau (ce qui peut échouer à nouveau et entraîner sa répétition).
Custom
ThreadFactory
peut être configuré, ce qui est utile:- Pour définir un nom de thread plus descriptif
- Pour définir le statut du démon de thread
- Pour définir la priorité des threads
Voici un exemple d'utilisation de ThreadPoolExecutor
Récupération de la valeur du calcul - Appelable
Si votre calcul produit une valeur de retour qui est requise plus tard, une simple tâche Runnable ne suffit pas. Dans ce cas, vous pouvez utiliser ExecutorService.submit(
Callable
<T>)
qui renvoie une valeur une fois l'exécution terminée.
Le service renverra un Future
que vous pourrez utiliser pour récupérer le résultat de l'exécution de la tâche.
// Submit a callable for execution
ExecutorService pool = anExecutorService;
Future<Integer> future = pool.submit(new Callable<Integer>() {
@Override public Integer call() {
//do some computation
return new Random().nextInt();
}
});
// ... perform other tasks while future is executed in a different thread
Lorsque vous avez besoin d'obtenir le résultat du futur, appelez future.get()
Attendez indéfiniment pour l'avenir pour finir avec un résultat.
try { // Blocks current thread until future is completed Integer result = future.get(); catch (InterruptedException || ExecutionException e) { // handle appropriately }
Attendez que l'avenir se termine, mais pas plus que le temps spécifié.
try { // Blocks current thread for a maximum of 500 milliseconds. // If the future finishes before that, result is returned, // otherwise TimeoutException is thrown. Integer result = future.get(500, TimeUnit.MILLISECONDS); catch (InterruptedException || ExecutionException || TimeoutException e) { // handle appropriately }
Si le résultat d'une tâche planifiée ou en cours d'exécution n'est plus requis, vous pouvez appeler Future.cancel(boolean)
pour l'annuler.
- L'appel de
cancel(false)
supprime simplement la tâche de la file d'attente des tâches à exécuter. - L'appel à
cancel(true)
interrompt également la tâche si elle est en cours d'exécution.
Planification de tâches à exécuter à une heure fixe, après un délai ou à plusieurs reprises
La classe ScheduledExecutorService
fournit une méthode permettant de planifier des tâches uniques ou répétées de plusieurs manières. L'exemple de code suivant suppose que le pool
a été déclaré et initialisé comme suit:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Outre les méthodes ExecutorService
normales, l'API ScheduledExecutorService
ajoute 4 méthodes qui planifient les tâches et renvoient ScheduledFuture
objets ScheduledFuture
. Ce dernier peut être utilisé pour récupérer des résultats (dans certains cas) et annuler des tâches.
Démarrer une tâche après un délai fixe
L'exemple suivant programme une tâche pour qu'elle démarre après dix minutes.
ScheduledFuture<Integer> future = pool.schedule(new Callable<>() {
@Override public Integer call() {
// do something
return 42;
}
},
10, TimeUnit.MINUTES);
Démarrer des tâches à un taux fixe
L'exemple suivant permet de planifier une tâche pour qu'elle démarre après dix minutes, puis de manière répétée à raison d'une fois par minute.
ScheduledFuture<?> future = pool.scheduleAtFixedRate(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
L'exécution de la tâche se poursuivra en fonction de la planification jusqu'à ce que le pool
soit fermé, que le future
soit annulé ou que l'une des tâches rencontre une exception.
Il est garanti que les tâches planifiées par un appel scheduledAtFixedRate
donné ne se chevaucheront pas dans le temps. Si une tâche prend plus de temps que la période prescrite, les exécutions de tâches suivantes et suivantes peuvent commencer tard.
Démarrer des tâches avec un délai fixe
L'exemple suivant permet de planifier une tâche pour qu'elle démarre après dix minutes, puis plusieurs fois avec un délai d'une minute entre la fin d'une tâche et la suivante.
ScheduledFuture<?> future = pool.scheduleWithFixedDelay(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
L'exécution de la tâche se poursuivra en fonction de la planification jusqu'à ce que le pool
soit fermé, que le future
soit annulé ou que l'une des tâches rencontre une exception.
Gérer l'exécution rejetée
Si
- vous essayez de soumettre des tâches à un exécuteur d'arrêt ou
- la file d'attente est saturée (uniquement possible avec des bornes) et le nombre maximum de threads a été atteint,
RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor)
sera appelé.
Le comportement par défaut est que vous obtiendrez une RejectedExecutionException lancée sur l'appelant. Mais il existe plus de comportements prédéfinis disponibles:
- ThreadPoolExecutor.AbortPolicy (par défaut, va lancer REE)
- ThreadPoolExecutor.CallerRunsPolicy (exécute une tâche sur le thread de l'appelant - le bloquant )
- ThreadPoolExecutor.DiscardPolicy (tâche silencieuse)
- ThreadPoolExecutor.DiscardOldestPolicy ( supprimer en silence la tâche la plus ancienne dans la file d'attente et réessayer l'exécution de la nouvelle tâche)
Vous pouvez les définir en utilisant l'un des constructeurs ThreadPool:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) // <--
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) // <--
Vous pouvez aussi implémenter votre propre comportement en étendant l'interface RejectedExecutionHandler :
void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
différences de gestion des exceptions submit () vs execute ()
Généralement, la commande execute () est utilisée pour les appels d'incendie et d'oubli (sans analyse du résultat) et la commande submit () est utilisée pour analyser le résultat d'un objet Future.
Nous devons être conscients de la différence clé entre les mécanismes de gestion des exceptions entre ces deux commandes.
Les exceptions de submit () sont avalées par framework si vous ne les avez pas capturées.
Exemple de code pour comprendre la différence:
Cas 1: soumettez la commande Runnable with execute (), qui signale l’exception.
import java.util.concurrent.*;
import java.util.*;
public class ExecuteSubmitDemo {
public ExecuteSubmitDemo() {
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(2);
//ExtendedExecutor service = new ExtendedExecutor();
for (int i = 0; i < 2; i++){
service.execute(new Runnable(){
public void run(){
int a = 4, b = 0;
System.out.println("a and b=" + a + ":" + b);
System.out.println("a/b:" + (a / b));
System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
}
});
}
service.shutdown();
}
public static void main(String args[]){
ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
}
}
class ExtendedExecutor extends ThreadPoolExecutor {
public ExtendedExecutor() {
super(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}
sortie:
creating service
a and b=4:0
a and b=4:0
Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Cas 2: Remplacez execute () par submit (): service.submit(new Runnable(){
Dans ce cas, les exceptions sont englouties par framework car la méthode run () ne les a pas capturées explicitement.
sortie:
creating service
a and b=4:0
a and b=4:0
Cas 3: Modifiez le newFixedThreadPool en ExtendedExecutor
//ExecutorService service = Executors.newFixedThreadPool(2);
ExtendedExecutor service = new ExtendedExecutor();
sortie:
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
a and b=4:0
java.lang.ArithmeticException: / by zero
J'ai montré cet exemple pour couvrir deux sujets: Utilisez votre ThreadPoolExecutor personnalisé et gérez Exectpion avec ThreadPoolExecutor personnalisé.
Autre solution simple au problème ci-dessus: Lorsque vous utilisez la commande normale ExecutorService & submit, obtenez l'objet Future de la commande submit (), appelez get () API sur Future. Catch les trois exceptions, qui ont été citées dans l'implémentation de la méthode afterExecute. Avantage de ThreadPoolExecutor personnalisé par rapport à cette approche: vous devez gérer le mécanisme de gestion des exceptions dans un seul endroit - Custom ThreadPoolExecutor.
Cas d'utilisation pour différents types de constructions de concurrence
ExecutorService executor = Executors.newFixedThreadPool(50);
C'est simple et facile à utiliser. Il cache les détails de bas niveau de
ThreadPoolExecutor
.Je préfère celui-ci lorsque le nombre de tâches
Callable/Runnable
est faible et que le cumul de tâches dans une file d'attente illimitée n'augmente pas la mémoire et ne dégrade pas les performances du système. Si vous avez des contraintesCPU/Memory
, je préfère utiliserThreadPoolExecutor
avec des contraintes de capacité etRejectedExecutionHandler
pour gérer le rejet des tâches.CountDownLatch
sera initialisé avec un nombre donné. Ce nombre est décrémenté par des appels à la méthodecountDown()
. Les threads attendant ce nombre pour atteindre zéro peuvent appeler l'une des méthodesawait()
. L'appel deawait()
bloque le thread jusqu'à ce que le compte atteigne zéro. Cette classe permet à un thread java d'attendre qu'un autre ensemble de threads termine ses tâches.Cas d'utilisation:
Atteindre un parallélisme maximal: nous souhaitons parfois lancer plusieurs threads en même temps pour obtenir un parallélisme maximal
Attendre que N threads se termine avant de commencer l'exécution
Détection de blocage
ThreadPoolExecutor : Il fournit plus de contrôle. Si l'application est limitée par le nombre de tâches Runnable / Callable en attente, vous pouvez utiliser la file d'attente limitée en définissant la capacité maximale. Une fois que la file d'attente atteint la capacité maximale, vous pouvez définir RejectionHandler. Java fournit quatre types de stratégies
RejectedExecutionHandler
.ThreadPoolExecutor.AbortPolicy
, le gestionnaire renvoie une exception RejectedExecutionException lors du rejet.ThreadPoolExecutor.CallerRunsPolicy`, le thread qui appelle exécute lui-même exécute la tâche. Cela fournit un mécanisme de contrôle de rétroaction simple qui ralentira le taux de soumission de nouvelles tâches.
Dans
ThreadPoolExecutor.DiscardPolicy
, une tâche qui ne peut pas être exécutée est simplement supprimée.ThreadPoolExecutor.DiscardOldestPolicy
, si l'exécuteur n'est pas arrêté, la tâche en tête de la file d'attente de travail est supprimée, puis l'exécution est exécutée à nouveau (ce qui peut échouer à nouveau, entraînant la répétition de l'opération).
Si vous souhaitez simuler le comportement CountDownLatch
, vous pouvez utiliser la méthode invokeAll()
.
Un autre mécanisme que vous n'avez pas cité est ForkJoinPool
Le
ForkJoinPool
été ajouté à Java en Java 7. LeForkJoinPool
est similaire au JavaExecutorService
mais avec une différence. LeForkJoinPool
permet aux tâches de fractionner leur travail en tâches plus petites qui sont ensuite soumises àForkJoinPool
. Le vol de tâche se produit dansForkJoinPool
lorsque des threads de travail libres volent des tâches de la file d'attente de threads de travail occupé.Java 8 a introduit une API supplémentaire dans ExecutorService pour créer un pool de vol de travail. Vous n'avez pas besoin de créer
RecursiveTask
etRecursiveAction
mais vous pouvez toujours utiliserForkJoinPool
.public static ExecutorService newWorkStealingPool()
Crée un pool de threads voleur de travail en utilisant tous les processeurs disponibles comme niveau de parallélisme cible.
Par défaut, il faudra un nombre de cœurs de CPU en paramètre.
Tous ces quatre mécanismes sont complémentaires. Selon le niveau de granularité que vous souhaitez contrôler, vous devez choisir les bons.
Attendez la fin de toutes les tâches dans ExecutorService
Jetons un coup d'oeil aux différentes options pour attendre l'achèvement des tâches soumises à l' exécuteur
- ExecutorService
invokeAll()
Exécute les tâches données, en retournant une liste de contrats à terme conservant leur statut et leurs résultats lorsque tout est terminé.
Exemple:
import java.util.concurrent.*;
import java.util.*;
public class InvokeAllDemo{
public InvokeAllDemo(){
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<MyCallable> futureList = new ArrayList<MyCallable>();
for (int i = 0; i < 10; i++){
MyCallable myCallable = new MyCallable((long)i);
futureList.add(myCallable);
}
System.out.println("Start");
try{
List<Future<Long>> futures = service.invokeAll(futureList);
} catch(Exception err){
err.printStackTrace();
}
System.out.println("Completed");
service.shutdown();
}
public static void main(String args[]){
InvokeAllDemo demo = new InvokeAllDemo();
}
class MyCallable implements Callable<Long>{
Long id = 0L;
public MyCallable(Long val){
this.id = val;
}
public Long call(){
// Add your business logic
return id;
}
}
}
Une aide à la synchronisation qui permet à un ou plusieurs threads d'attendre qu'un ensemble d'opérations soit exécuté dans d'autres threads.
Un CountDownLatch est initialisé avec un nombre donné. Les méthodes d’attente bloquent jusqu’à ce que le nombre actuel atteigne zéro en raison des
countDown()
méthodecountDown()
, après quoi tous les threads en attente sont libérés et toutes les invocations d’attente suivantes sontcountDown()
immédiatement. Ceci est un phénomène à un coup - le compte ne peut pas être réinitialisé. Si vous avez besoin d'une version qui réinitialise le compte, envisagez d'utiliser un CyclicBarrier .ForkJoinPool ou
newWorkStealingPool()
dans les exécuteursParcourez tous les objets
Future
créés après la soumission àExecutorService
Méthode recommandée pour arrêter la page de documentation d'Oracle d' ExecutorService :
void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); }
shutdown():
lance un arrêt ordonné au cours duquel les tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche ne sera acceptée.shutdownNow():
tente d'arrêter toutes les tâches en cours d'exécution, arrête le traitement des tâches en attente et renvoie une liste des tâches en attente d'exécution.Dans l'exemple ci-dessus, si vos tâches prennent plus de temps, vous pouvez changer si condition à condition
Remplacer
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
avec
while(!pool.awaitTermination(60, TimeUnit.SECONDS)) { Thread.sleep(60000);
}
Cas d'utilisation pour différents types d'ExecutorService
Les exécuteurs retournent différents types de ThreadPools répondant à des besoins spécifiques.
public static ExecutorService newSingleThreadExecutor()
Crée un exécuteur qui utilise un seul thread de travail opérant sur une file d'attente illimitée
Il y a une différence entre
newFixedThreadPool(1)
etnewSingleThreadExecutor()
comme le dit le doc java pour ce dernier:Contrairement à newFixedThreadPool (1), par ailleurs équivalent, l'exécuteur renvoyé est garanti ne pas pouvoir être reconfiguré pour utiliser des threads supplémentaires.
Ce qui signifie qu'un
newFixedThreadPool
peut être reconfiguré plus tard dans le programme par:((ThreadPoolExecutor) fixedThreadPool).setMaximumPoolSize(10)
Ceci n'est pas possible pournewSingleThreadExecutor
Cas d'utilisation:
- Vous souhaitez exécuter les tâches soumises dans une séquence.
- Vous n'avez besoin que d'un seul thread pour gérer toute votre demande
Les inconvénients:
- La file d'attente sans limite est nuisible
public static ExecutorService newFixedThreadPool(int nThreads)
Crée un pool de threads qui réutilise un nombre fixe de threads exécutant une file d'attente sans limites partagée. À tout moment, au plus les threads nThreads seront des tâches de traitement actives. Si des tâches supplémentaires sont soumises lorsque tous les threads sont actifs, ils attendent dans la file d'attente jusqu'à ce qu'un thread soit disponible
Cas d'utilisation:
- Utilisation efficace des noyaux disponibles. Configurez
nThreads
enRuntime.getRuntime().availableProcessors()
- Lorsque vous décidez que ce nombre de thread ne doit pas dépasser un nombre dans le pool de threads
Les inconvénients:
- La file d'attente sans limite est nuisible.
- Utilisation efficace des noyaux disponibles. Configurez
public static ExecutorService newCachedThreadPool()
Crée un pool de threads qui crée de nouveaux threads en fonction des besoins, mais réutilise les threads précédemment construits lorsqu'ils sont disponibles
Cas d'utilisation:
- Pour les tâches asynchrones de courte durée
Les inconvénients:
- La file d'attente sans limite est nuisible.
- Chaque nouvelle tâche créera un nouveau thread si tous les threads existants sont occupés. Si la tâche dure longtemps, un plus grand nombre de threads sera créé, ce qui dégradera les performances du système. Alternative dans ce cas:
newFixedThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
Crée un pool de threads pouvant planifier des commandes à exécuter après un délai donné ou à exécuter périodiquement.
Cas d'utilisation:
- Gestion des événements récurrents avec des retards, qui se produiront à un certain intervalle de temps
Les inconvénients:
- La file d'attente sans limite est nuisible.
5.
public static ExecutorService newWorkStealingPool()
Crée un pool de threads voleur de travail en utilisant tous les processeurs disponibles comme niveau de parallélisme cible
Cas d'utilisation:
- Pour diviser et conquérir type de problèmes.
- Utilisation efficace des threads inactifs. Les threads inactifs volent les tâches des threads occupés.
Les inconvénients:
- La taille de la file d'attente non liée est dangereuse.
Vous pouvez voir un inconvénient commun à tous ces ExecutorService: la file d'attente illimitée. Ceci sera adressé avec ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Avec ThreadPoolExecutor
, vous pouvez
- Contrôler dynamiquement la taille du pool de threads
- Définir la capacité de
BlockingQueue
- Définir
RejectionExecutionHander
lorsque la file d'attente est pleine -
CustomThreadFactory
pour ajouter des fonctionnalités supplémentaires lors de la création de threads(public Thread newThread(Runnable r)
Utilisation des pools de threads
Les pools de threads sont principalement utilisés pour appeler des méthodes dans ExecutorService
.
Les méthodes suivantes peuvent être utilisées pour soumettre du travail à l'exécution:
Méthode | La description |
---|---|
submit | Exécute le travail soumis et retourne un futur qui peut être utilisé pour obtenir le résultat |
execute | Exécutez la tâche dans le futur sans obtenir de valeur de retour |
invokeAll | Exécutez une liste de tâches et retournez une liste de futures |
invokeAny | Exécute tout mais retourne uniquement le résultat de celui qui a réussi (sans exceptions) |
Une fois que vous avez terminé avec le pool de threads, vous pouvez appeler shutdown()
pour terminer le pool de threads. Cela exécute toutes les tâches en attente. Attendre que toutes les tâches awaitTermination
isShutdown()
vous pouvez faire une boucle autour de awaitTermination
ou isShutdown()
.