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.

Java SE 8
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:

  1. 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.

  2. Vous pouvez utiliser des stratégies existantes ou créer de nouvelles stratégies de rejet.

    1. Dans le ThreadPoolExecutor.AbortPolicy par défaut, le gestionnaire renvoie une exception RejectedExecutionException à l'exécution lors du rejet.

    2. 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.

    3. Dans ThreadPoolExecutor.DiscardPolicy , une tâche qui ne peut pas être exécutée est simplement supprimée.

    4. 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).

  3. Custom ThreadFactory peut être configuré, ce qui est utile:

    1. Pour définir un nom de thread plus descriptif
    2. Pour définir le statut du démon de thread
    3. 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

  1. vous essayez de soumettre des tâches à un exécuteur d'arrêt ou
  2. 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

  1. ExecutorService

    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 contraintes CPU/Memory , je préfère utiliser ThreadPoolExecutor avec des contraintes de capacité et RejectedExecutionHandler pour gérer le rejet des tâches.

  2. CountDownLatch

    CountDownLatch sera initialisé avec un nombre donné. Ce nombre est décrémenté par des appels à la méthode countDown() . Les threads attendant ce nombre pour atteindre zéro peuvent appeler l'une des méthodes await() . L'appel de await() 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:

    1. Atteindre un parallélisme maximal: nous souhaitons parfois lancer plusieurs threads en même temps pour obtenir un parallélisme maximal

    2. Attendre que N threads se termine avant de commencer l'exécution

    3. Détection de blocage

  1. 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 .

    1. ThreadPoolExecutor.AbortPolicy , le gestionnaire renvoie une exception RejectedExecutionException lors du rejet.

    2. 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.

    3. Dans ThreadPoolExecutor.DiscardPolicy , une tâche qui ne peut pas être exécutée est simplement supprimée.

    4. 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() .

  1. Un autre mécanisme que vous n'avez pas cité est ForkJoinPool

    Le ForkJoinPool été ajouté à Java en Java 7. Le ForkJoinPool est similaire au Java ExecutorService mais avec une différence. Le ForkJoinPool 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 dans ForkJoinPool 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 et RecursiveAction mais vous pouvez toujours utiliser ForkJoinPool .

    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

  1. 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;
        }
    }
}
  1. CountDownLatch

    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éthode countDown() , après quoi tous les threads en attente sont libérés et toutes les invocations d’attente suivantes sont countDown() 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 .

  2. ForkJoinPool ou newWorkStealingPool() dans les exécuteurs

  3. Parcourez tous les objets Future créés après la soumission à ExecutorService

  4. 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.

  1. 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) et newSingleThreadExecutor() 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 pour newSingleThreadExecutor

    Cas d'utilisation:

    1. Vous souhaitez exécuter les tâches soumises dans une séquence.
    2. Vous n'avez besoin que d'un seul thread pour gérer toute votre demande

    Les inconvénients:

    1. La file d'attente sans limite est nuisible
  2. 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:

    1. Utilisation efficace des noyaux disponibles. Configurez nThreads en Runtime.getRuntime().availableProcessors()
    2. Lorsque vous décidez que ce nombre de thread ne doit pas dépasser un nombre dans le pool de threads

    Les inconvénients:

    1. La file d'attente sans limite est nuisible.
  3. 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:

    1. Pour les tâches asynchrones de courte durée

    Les inconvénients:

    1. La file d'attente sans limite est nuisible.
    2. 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
  1. 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:

    1. Gestion des événements récurrents avec des retards, qui se produiront à un certain intervalle de temps

    Les inconvénients:

    1. 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:

    1. Pour diviser et conquérir type de problèmes.
    2. Utilisation efficace des threads inactifs. Les threads inactifs volent les tâches des threads occupés.

    Les inconvénients:

    1. 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

  1. Contrôler dynamiquement la taille du pool de threads
  2. Définir la capacité de BlockingQueue
  3. Définir RejectionExecutionHander lorsque la file d'attente est pleine
  4. 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() .



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow