J'apprends à utiliser ExectorService
pour mettre en commun threads
et envoyer des tâches. J'ai un programme simple ci-dessous
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.TimeUnit;
class Processor implements Runnable {
private int id;
public Processor(int id) {
this.id = id;
}
public void run() {
System.out.println("Starting: " + id);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("sorry, being interupted, good bye!");
System.out.println("Interrupted "+Thread.currentThread().getName());
e.printStackTrace();
}
System.out.println("Completed: " + id);
}
}
public class ExecutorExample {
public static void main(String[] args) {
Boolean isCompleted=false;
ExecutorService executor = Executors.newFixedThreadPool(2);
for(int i=0; i<5; i++) {
executor.execute(new Processor(i));
}
//executor does not accept any more tasks but the submitted tasks continue
executor.shutdown();
System.out.println("All tasks submitted.");
try {
//wait for the exectutor to terminate normally, which will return true
//if timeout happens, returns false, but this does NOT interrupt the threads
isCompleted=executor.awaitTermination(100, TimeUnit.SECONDS);
//this will interrupt thread it manages. catch the interrupted exception in the threads
//If not, threads will run forever and executor will never be able to shutdown.
executor.shutdownNow();
} catch (InterruptedException e) {
}
if (isCompleted){
System.out.println("All tasks completed.");
}
else {
System.out.println("Timeout "+Thread.currentThread().getName());
}
}
}
Il ne fait rien d'extraordinaire, mais crée deux threads
et soumet 5 tâches au total. Une fois que chaque thread
a terminé sa tâche, il prend la suivante, Dans le code ci-dessus, j’utilise executor.submit
. J'ai aussi changé pour executor.execute
. Mais je ne vois aucune différence dans la sortie. En quoi les méthodes submit and execute
sont-elles différentes? C'est ce que dit API
La méthode submit étend la méthode de base Executor.execute (Java.lang.Runnable) en créant et en renvoyant un avenir pouvant être utilisé pour annuler l'exécution et/ou attendre son achèvement. Les méthodes invokeAny et invokeAll effectuent les formes d'exécution en bloc les plus utiles, en exécutant un ensemble de tâches, puis en attendant la réalisation d'au moins une ou de toutes les tâches. (La classe ExecutorCompletionService peut être utilisée pour écrire des variantes personnalisées de ces méthodes.)
Mais ce n'est pas clair pour moi comme ce que cela signifie exactement?
Comme vous le voyez dans JavaDoc, execute(Runnable)
ne renvoie rien.
Cependant, submit(Callable<T>)
renvoie un objet Future
qui vous permet d'annuler par programme le thread en cours d'exécution par la suite et d'obtenir la T
renvoyée à la fin de la Callable
. Voir JavaDoc of Future pour plus de détails
Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);
De plus, If future.get() == null
et ne lève aucune exception, puis Runnable a été exécuté avec succès
La différence est que execute
démarre simplement la tâche sans plus tarder, alors que submit
renvoie un objet Future
pour gérer la tâche. Vous pouvez effectuer les opérations suivantes avec l’objet Future
:
cancel
.get
.L'interface Future
est plus utile si vous soumettez une Callable
au pool. La valeur de retour de la méthode call
sera renvoyée lorsque vous appelez Future.get
. Si vous ne conservez pas de référence à la variable Future
, il n'y a pas de différence.
execute:
Utilisez-le pour tirer et oublier des appels
submit:
Utilisez-le pour inspecter le résultat de l'appel de la méthode et effectuez l'action appropriée sur Future
objecté renvoyé par l'appel
Différence majeure: manipulation Exception
submit()
cache Exception
non manipulé dans le cadre même.
execute()
jette Exception
non manipulé.
Solution de gestion des exceptions avec submit()
Enveloppez votre Callable or Runnable code in try{} catch{} block
OU
Garder future.get() call in try{} catch{} block
OU
implémentez votre propre méthode ThreadPoolExecutor
et substituez la méthode afterExecute
En ce qui concerne tournée d'autres questions sur
Exécute les tâches données en renvoyant une liste des contrats à terme contenant leur statut et leurs résultats une fois que tout est terminé ou que le délai d'attente expire, selon la première éventualité.
Exécute les tâches données, en renvoyant le résultat de celui qui s’est terminé avec succès (c’est-à-dire sans lever d’exception), le cas échéant, avant l’expiration du délai imparti.
Utilisez invokeAll
si vous souhaitez attendre que toutes les tâches soumises soient terminées.
Utilisez invokeAny
si vous souhaitez mener à bien une tâche sur N tâches soumises. Dans ce cas, les tâches en cours seront annulées si l’une des tâches s’achève avec succès.
Article associé avec exemple de code:
Choisissez entre submit de ExecutorService et execute de ExecutorService
Submit - Renvoie un objet Future qui peut être utilisé pour vérifier le résultat de la tâche soumise. Peut être utilisé pour annuler ou vérifier isDone etc.
Execute - ne retourne rien.
Une différence principale entre les méthodes submit () et execute () est qu'ExecuterService.submit () peut renvoyer le résultat du calcul car il a un type de retour de type Future, mais la méthode execute () ne peut rien retourner car son type de retour est void. L'interface principale du framework Executor de Java 1.5 est l'interface Executor qui définit la méthode execute (tâche pouvant être exécutée), dont le but principal est de séparer la tâche de son exécution.
Toute tâche soumise à l'exécuteur peut être exécutée par le même thread, un thread de travail d'un pool de threads ou tout autre thread.
D'autre part, la méthode submit () est définie dans l'interface ExecutorService, qui est une sous-interface de Executor. Elle ajoute la fonctionnalité de fermeture du pool de threads, ainsi que la méthode submit () qui peut accepter une tâche appelable et renvoyer un résultat. de calcul.
Similitudes entre execute () et submit () également:
Hormis le fait que la méthode submit () peut renvoyer la sortie et que execute () ne le peut pas, voici d'autres différences notables entre ces deux méthodes clés du framework Executor de Java 5.
en gros, les deux appels sont exécutés. Si vous voulez un objet futur, vous devez appeler la méthode submit () ici du doc
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
comme vous pouvez le constater, Java n'a aucun moyen de démarrer un thread autre que l'appel de la méthode run (), IMO. depuis que j'ai aussi trouvé que la méthode Callable.call()
est appelée à l'intérieur de la méthode run()
. Par conséquent, si l'objet est appelable, il appelle toujours la méthode run()
, qui à son tour appelle la méthode call()
.
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}