web-dev-qa-db-fra.com

Quelle est la différence entre ExecutorService.submit et ExecutorService.execute dans ce code en Java?

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?

34
brain storm

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

38
dkatzel

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:

  • Annulez la tâche prématurément, avec la méthode cancel.
  • Attendez la fin de l'exécution de la tâche avec 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.

40
tbodt

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

  1. Enveloppez votre Callable or Runnable code in try{} catch{} block

    OU 

  2. Garder future.get() call in try{} catch{} block

    OU 

  3. implémentez votre propre méthode ThreadPoolExecutor et substituez la méthode afterExecute

En ce qui concerne tournée d'autres questions sur

invokeAll :

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

invokeAny :

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

12
Ravindra babu

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. 

5
Preetham R U

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:

  1. Les méthodes submit () et execute () sont utilisées pour soumettre une tâche au framework Executor pour une exécution asynchrone.
  2. Submit () et execute () peuvent accepter une tâche exécutable.
  3. Vous pouvez accéder à submit () et à execute () à partir de l'interface ExecutorService car elle étend également l'interface Executor qui déclare la méthode execute ().

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.

  1. Submit () peut accepter les tâches exécutables et appelables, mais execute () ne peut accepter que la tâche exécutable.
  2. La méthode submit () est déclarée dans l'interface ExecutorService, tandis que la méthode execute () est déclarée dans l'interface Executor.
  3. Le type de retour de la méthode submit () est un objet Future, mais le type de retour de la méthode execute () est void.
2
amitkumar12788

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);
    }
}
0
amarnath harish