116 votes

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

J'apprends à utiliser ExectorService à la piscine 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. Après chaque thread termine sa tâche, il prend la suivante, Dans le code ci-dessus, j'utilise executor.submit . J'ai également changé pour executor.execute . Mais je ne vois aucune différence dans la sortie. De quelle manière les submit y execute méthodes différentes ? Voici ce que le API dit

La méthode submit étend la méthode de base Executor.execute(java.lang.Runnable) en créant et en renvoyant un Future qui peut être utilisé pour annuler l'exécution et/ou attendre l'achèvement. Les méthodes invokeAny et invokeAll exécutent les formes les plus courantes d'exécution en masse, en exécutant un ensemble de tâches et en attendant qu'au moins l'une d'entre elles, voire toutes, se termine. (La classe ExecutorCompletionService peut être utilisée pour écrire des variantes personnalisées de ces méthodes).

Mais je ne comprends pas bien ce que cela signifie exactement ?

5voto

Preetham R U Points 286

Submit - Renvoie un objet Future, qui peut être utilisé pour vérifier le résultat de la tâche soumise. Il peut être utilisé pour annuler ou vérifier isDone, etc.

Exécuter - ne renvoie rien.

1voto

Zhang Amos Points 11

En execute(Runnable command) est la méthode implémentée de l'interface Executor . Cela signifie qu'il suffit d'exécuter la commande et de ne rien obtenir en retour.

ExecutorService a ses propres méthodes pour démarrer les tâches : submit , invokeAny y invokeAll qui ont toutes Callable comme leurs principales cibles. Bien qu'il y ait des méthodes ayant Runnable comme entrée, actulaly Runnable sera adapté à Callable dans la méthode. pourquoi Callable ? Parce que nous pouvons obtenir un Future<T> le résultat après la soumission de la tâche.

Mais lorsque vous transformez un Runnable à un Callable Le résultat que vous obtenez est juste la valeur que vous passez :

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

Donc, quel est l'intérêt de passer une Runnable à soumettre au lieu de simplement obtenir le résultat lorsque la tâche est terminée ? Parce qu'il y a une méthode qui a seulement Runnable comme paramètre sans résultat particulier.

Lisez la javadoc de Future :

Si vous souhaitez utiliser un Future pour des raisons d'annulation mais sans fournir de résultat utilisable, vous pouvez déclarer des types de la forme Future<?> et renvoyer null comme résultat de la tâche sous-jacente.

Donc, si vous voulez juste exécuter une Runnable sans qu'aucune valeur ne soit renvoyée, vous pouvez utiliser execute() .

si vous voulez exécuter un Callable tâche, ou

si vous voulez exécuter un Runnable avec un résultat spécifié comme symbole d'achèvement, ou

si vous voulez exécuter une tâche et avoir la possibilité de l'annuler,

vous devez utiliser submit() .

0voto

amarnath harish Points 734

En fait, les deux appels s'exécutent, si vous voulez un objet futur, vous devez appeler la méthode submit(). Voici un extrait de la documentation

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 voir, Java n'a pas vraiment de moyen de démarrer un thread autre que d'appeler la méthode run(), IMO. comme j'ai aussi trouvé que Callable.call() est appelée à l'intérieur de run() Donc, si l'objet est appelable, il appellera toujours la méthode run() qui, à son tour, appelle la méthode call() méthode de la doc.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X