web-dev-qa-db-fra.com

Choisissez entre soumettre et ExecutorService exécuter ou exécuter

Comment choisir entre ExecutorService submit ou execute , si la valeur renvoyée ne me concerne pas ?

Si je teste les deux, je ne vois aucune différence entre les deux, à l'exception de la valeur renvoyée.

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

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

Il existe une différence concernant le traitement des exceptions/erreurs.

Une tâche mise en file d'attente avec execute() et générant une certaine Throwable fera appeler UncaughtExceptionHandler pour le Thread exécutant la tâche. La valeur par défaut UncaughtExceptionHandler, qui imprime généralement la trace de pile Throwable sur System.err, Sera invoquée si aucun gestionnaire personnalisé n'a été installé.

D'autre part, un Throwable généré par une tâche mise en file d'attente avec submit() liera le Throwable au Future produit à partir de l'appel à submit(). Appeler get() sur ce Future lancera un ExecutionException avec l'original Throwable comme cause (accessible en appelant getCause() sur le ExecutionException).

197
hochraldo

execute: Utilisez-le pour déclencher et oublier des appels

submit: Utilisez-le pour inspecter le résultat de l'appel de la méthode et prendre les mesures appropriées sur Future objecté. retourné par l'appel

De javadocs

submit(Callable<T> task)

Soumet une tâche renvoyant une valeur à exécuter et renvoie un avenir 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 futur représentant cette tâche.

void execute(Runnable command)

Exécute la commande donnée à un moment donné dans le futur. La commande peut être exécutée dans un nouveau thread, dans un thread en pool ou dans le thread appelant, à la discrétion de l'implémentation de l'exécuteur.

Vous devez prendre des précautions lorsque vous utilisez submit(). Il cache une exception dans le cadre même, sauf si vous incorporez votre code de tâche dans le bloc try{} catch{}.

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();
    }
}

sortie:

Java ExecuteSubmitDemo
creating service
a and b=4:0

Même code jeté en remplaçant submit() par execute ():

Remplacer

service.submit(new Runnable(){

avec

service.execute(new Runnable(){

sortie:

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 ce type de scénario en utilisant submit ()?

  1. Intégrez votre code de tâche ( implémentation exécutable ou appelable) avec try {} catch {} code bloqué
  2. Implémenter 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);
   }
 }

sortie:

Java ExecuteSubmitDemo
creating service
a and b=4:0
Java.lang.ArithmeticException: / by zero
59
Ravindra babu

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

11
Steven

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} pouvant être utilisé pour annuler l'exécution et/ou attendre son achèvement.

Personnellement, je préfère utiliser execute 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'implémentation ExecutorService, l'implémentation principale renvoyée par l'appel à Executors.newSingleThreadedExecutor() est un ThreadPoolExecutor.

Les appels submit sont fournis par son parent AbstractExecutorService et tous les appels s’exécutent en interne. execute est remplacé/fourni par le ThreadPoolExecutor directement.

7
Syntax

De la Javadoc :

La commande peut être exécutée dans un nouveau thread, dans un thread en pool ou dans le thread appelant, à la discrétion de l'implémentation de l'exécuteur.

Donc, en fonction de l'implémentation de Executor, il se peut que le thread en cours de soumission se bloque pendant l'exécution de la tâche.

2
rxg

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

  • En soumettant une tâche (par opposition à son exécution), vous récupérez un futur 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 type de retour id void)
  • execute s'attend à un Runnable alors que submit peut prendre en argument un Runnable ou un Callable (pour plus d'informations sur la différence entre les deux - voir ci-dessous).
  • execute ajoute immédiatement toutes les exceptions non vérifiées (il ne peut pas lancer les exceptions vérifiées !!!), alors que submit se lie n'importe quel une sorte d'exception pour le futur qui retourne en conséquence, et uniquement lorsque vous appelez future.get(), une exception (encapsulée) sera levée. Le Throwable que vous aurez est une instance de ExecutionException et si vous appelez getCause() de cet objet, il retournera le Throwable d'origine.

Quelques autres points (liés):

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

En résumé, il est préférable d’utiliser submit avec un Callable (par opposition à execute avec un Runnable). Et je citerai "La concurrence de Java dans la pratique" de Brian Goetz:

6.3.2 Tâches qui génèrent des résultats: appelables et futures

La structure Executor utilise Runnable comme représentation de base des tâches. Runnable est une abstraction assez limitante; run ne peut pas renvoyer de valeur ni renvoyer d'exceptions vérifiées, bien qu'il puisse avoir des effets secondaires tels qu'écrire dans un fichier journal ou placer un résultat dans une structure de données partagée. De nombreuses tâches sont effectivement des calculs différés - exécution d'une requête de base de données, récupération 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, retourne une valeur et qu'il risque de lever une exception.7 Executors inclut plusieurs méthodes d'utilitaire pour englober d'autres types de tâches, notamment Runnable. et Java.security.PrivilegedAction, avec un objet appelable.

1
alfasin