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());
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
).
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 ()?
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
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.
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.
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.
La réponse complète est une composition de deux réponses publiées ici (plus un peu "extra"):
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):
submit
ne nécessite pas de renvoyer un résultat, vous pouvez toujours utiliser Callable<Void>
(au lieu d'utiliser un Runnable
).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.