Choisissez entre la soumission d'ExecutorService et l'exécution d'ExecutorService

194

Comment dois-je choisir entre la soumission ou l' exécution d' ExecutorService , si la valeur renvoyée ne me concerne pas?

Si je teste les deux, je n'ai vu aucune différence entre les deux, sauf la valeur renvoyée.

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
Cheok Yan Cheng
la source

Réponses:

204

Il existe une différence concernant la gestion des exceptions / erreurs.

Une tâche avec la file d' attente execute()qui génère certains Throwableentraînera la UncaughtExceptionHandlerpour la Threadexécution de la tâche à invoquer. La valeur par défaut UncaughtExceptionHandler, qui imprime généralement la Throwabletrace de la pile sur System.err, sera appelée si aucun gestionnaire personnalisé n'a été installé.

D'autre part, un Throwablegénéré par une tâche mise en file d'attente avec submit()liera le Throwableau Futurequi a été produit à partir de l'appel à submit(). Faire appel get()à cela Futurelancera un ExecutionExceptionavec l'original Throwablecomme cause (accessible en appelant getCause()le ExecutionException).

Hochraldo
la source
19
Notez que ce comportement n'est pas garanti car il dépend du fait que vous soyez encapsulé ou non Runnabledans un Task, sur lequel vous n'avez peut-être aucun contrôle. Par exemple, si votre Executorest en fait un ScheduledExecutorService, votre tâche sera encapsulée en interne dans a Futureet les non interceptés Throwableseront liés à cet objet.
rxg
4
Je veux dire «enveloppé dans un Futureou pas», bien sûr. Voir le Javadoc pour ScheduledThreadPoolExecutor # execute , par exemple.
rxg
61

exécuter : utilisez-le pour tirer et oublier les appels

submit : utilisez-le pour inspecter le résultat de l'appel de méthode et prendre les mesures appropriées sur l'objetFuturerenvoyé par l'appel

de javadocs

submit(Callable<T> task)

Soumet une tâche de renvoi de valeur pour exécution et renvoie un Future représentant les résultats en attente de la tâche.

Future<?> submit(Runnable task)

Soumet une tâche exécutable pour exécution et renvoie un Future représentant cette tâche.

void execute(Runnable command)

Exécute la commande donnée à un moment donné dans le futur. La commande peut s'exécuter dans un nouveau thread, dans un thread groupé ou dans le thread appelant, à la discrétion de l'implémentation Executor.

Vous devez prendre des précautions lors de l'utilisation submit(). Il masque l'exception dans le cadre lui-même, sauf si vous incorporez votre code de tâche dans un try{} catch{}bloc.

Exemple de code: Ce code avale Arithmetic exception : / by zero.

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(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();
    }
}

production:

java ExecuteSubmitDemo
creating service
a and b=4:0

Le même code lève en remplaçant submit()par execute():

Remplacer

service.submit(new Runnable(){

avec

service.execute(new Runnable(){

production:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        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)

Comment gérer ces types de scénarios lors de l'utilisation de submit ()?

  1. Intégrez votre code de tâche ( implémentation exécutable ou appelable) avec le code de bloc try {} catch {}
  2. Mettre en place CustomThreadPoolExecutor

Nouvelle solution:

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(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);
   }
 }

production:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
Ravindra babu
la source
Bonne explication nette. Bien que l'extension ne soit PAS vraiment nécessaire. Juste cet objet futur doit être consommé pour savoir si la tâche a réussi ou non. ainsi, utilisez submit () si vous prévoyez de consommer Future <t> sinon utilisez simplement execute ()
prash
11

si vous ne vous souciez pas du type de retour, utilisez execute. c'est la même chose que soumettre, juste sans le retour de Future.

Steven
la source
15
Ce n'est pas correct selon la réponse acceptée. La gestion des exceptions est une différence assez significative.
Zero3
7

Tiré du Javadoc:

La méthode submitétend la méthode de base {@link Executor # execute} en créant et en renvoyant un {@link Future} qui peut être utilisé pour annuler l'exécution et / ou attendre la fin.

Personnellement, je préfère l'utilisation d'exécuter parce que cela semble plus déclaratif, bien que ce soit vraiment une question de préférence personnelle.

Pour donner plus d'informations: dans le cas de l' ExecutorServiceimplémentation, l'implémentation principale renvoyée par l'appel à Executors.newSingleThreadedExecutor()est un ThreadPoolExecutor.

Les submitappels sont fournis par son parent AbstractExecutorServiceet tous les appels s'exécutent en interne. execute est remplacé / fourni par ThreadPoolExecutordirectement.

Syntaxe
la source
2

Depuis le Javadoc :

La commande peut s'exécuter dans un nouveau thread, dans un thread groupé ou dans le thread appelant, à la discrétion de l'implémentation Executor.

Ainsi, en fonction de l'implémentation de, Executorvous constaterez peut-être que le thread de soumission se bloque pendant l'exécution de la tâche.

rxg
la source
1

La réponse complète est une composition de deux réponses qui ont été publiées ici (plus un peu "extra"):

  • En soumettant une tâche (plutôt qu'en l'exécutant), vous récupérez un avenir qui peut être utilisé pour obtenir le résultat ou annuler l'action. Vous n'avez pas ce type de contrôle lorsque vous execute(car son identifiant de type de retour void)
  • executeattend un Runnablemoment submitpeut prendre un Runnableou un Callablecomme argument (pour plus d'informations sur la différence entre les deux - voir ci-dessous).
  • executebulles vers le haut toutes les exceptions non vérifiées tout de suite (il ne peut pas lancer d'exceptions vérifiées !!!), tandis que submitlie tout type d'exception au futur qui retourne en conséquence, et seulement lorsque vous appelez future.get()une exception (encapsulée) sera levée. Le Throwable que vous obtiendrez est une instance de ExecutionExceptionet si vous appelez cet objetgetCause() il renverra le Throwable d'origine.

Quelques autres points (liés):

  • Même si la tâche que vous souhaitez submitne nécessite pas de retourner un résultat, vous pouvez toujours utiliser Callable<Void>(au lieu d'utiliser unRunnable ).
  • L'annulation des tâches peut être effectuée à l'aide du mécanisme d' interruption . Voici un exemple de mise en œuvre d'une politique d'annulation

Pour résumer, c'est une meilleure pratique à utiliser submitavec a Callable(plutôt executequ'avec a Runnable). Et je citerai «la concurrence Java en pratique» de Brian Goetz:

6.3.2 Tâches porteuses de résultats: appelables et futures

Le framework Executor utilise Runnable comme représentation de base des tâches. Runnable est une abstraction assez limitative; run ne peut pas retourner une valeur ou lever des exceptions vérifiées, bien que cela puisse avoir des effets secondaires tels que l'écriture dans un fichier journal ou le placement d'un résultat dans une structure de données partagée. De nombreuses tâches sont en fait des calculs différés - exécution d'une requête de base de données, extraction d'une ressource sur le réseau ou calcul d'une fonction compliquée. Pour ces types de tâches, Callable est une meilleure abstraction: il s'attend à ce que le point d'entrée principal, call, renvoie une valeur et prévoit qu'il pourrait lever une exception.7 Les exécuteurs incluent plusieurs méthodes utilitaires pour encapsuler d'autres types de tâches, y compris Runnable et java.security.PrivilegedAction, avec un Callable.

Alfasin
la source
1

Juste en ajoutant à la réponse acceptée-

Cependant, les exceptions lancées à partir des tâches parviennent au gestionnaire d'exceptions non interceptées uniquement pour les tâches soumises avec execute (); pour les tâches soumises avec submit () au service exécuteur, toute exception levée est considérée comme faisant partie du statut de retour de la tâche.

La source

abhihello123
la source