Wählen Sie zwischen ExecutorService Submit und ExecutorService Execute


193

Wie soll ich wählen zwischen ExecutorService des einreichen oder ausführen , wenn der zurückgegebene Wert nicht meine Sorge ist?

Wenn ich beide teste, sehe ich keine Unterschiede zwischen den beiden außer dem zurückgegebenen Wert.

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

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

Antworten:


203

Es gibt einen Unterschied in Bezug auf die Ausnahme- / Fehlerbehandlung.

Eine Task, die in der Warteschlange steht und execute()einige generiert, Throwableführt dazu, UncaughtExceptionHandlerdass das ThreadAusführen der Task aufgerufen wird. Die Standardeinstellung UncaughtExceptionHandler, auf die normalerweise die ThrowableStapelverfolgung gedruckt System.errwird, wird aufgerufen, wenn kein benutzerdefinierter Handler installiert wurde.

Auf der anderen Seite bindet eine Throwablevon einer in der Warteschlange befindlichen Aufgabe generierte Aufgabe submit()die Throwablean die Aufgabe Future, die aus dem Aufruf von erzeugt wurde submit(). Wenn Sie get()das aufrufen , Futurewird ein ExecutionExceptionmit dem Original Throwableals Ursache ausgelöst (zugänglich durch Aufrufen getCause()des ExecutionException).


19
Beachten Sie, dass dieses Verhalten nicht garantiert ist, da es davon abhängt, ob Sie Runnablein ein System eingewickelt werden Taskoder nicht, über das Sie möglicherweise keine Kontrolle haben. Wenn Sie Executorbeispielsweise tatsächlich eine sind ScheduledExecutorService, wird Ihre Aufgabe intern in eine eingeschlossen, Futureund nicht erfasste Throwables werden an dieses Objekt gebunden.
RXG

4
Ich meine Futurenatürlich "eingewickelt in ein oder nicht". Siehe beispielsweise Javadoc für ScheduledThreadPoolExecutor # execute .
RXG

59

Ausführen : Verwenden Sie es für Feuer und vergessen Sie Anrufe

submit : Verwenden Sie diese Option, um das Ergebnis des Methodenaufrufs zu überprüfen und geeignete Maßnahmen fürFuturevom Aufruf zurückgegebene Objekte zuergreifen

Von Javadocs

submit(Callable<T> task)

Sendet eine wertrückgebende Aufgabe zur Ausführung und gibt eine Zukunft zurück, die die ausstehenden Ergebnisse der Aufgabe darstellt.

Future<?> submit(Runnable task)

Sendet eine ausführbare Aufgabe zur Ausführung und gibt eine Zukunft zurück, die diese Aufgabe darstellt.

void execute(Runnable command)

Führt den angegebenen Befehl zu einem späteren Zeitpunkt aus. Der Befehl kann nach Ermessen der Executor-Implementierung in einem neuen Thread, in einem Pool-Thread oder im aufrufenden Thread ausgeführt werden.

Sie müssen bei der Verwendung Vorsichtsmaßnahmen treffen submit(). Es verbirgt Ausnahmen im Framework selbst, es sei denn, Sie binden Ihren Aufgabencode in try{} catch{}Block ein.

Beispielcode: Dieser Code schluckt 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();
    }
}

Ausgabe:

java ExecuteSubmitDemo
creating service
a and b=4:0

Gleicher Code wird durch Ersetzen submit()durch execute() ausgelöst :

Ersetzen

service.submit(new Runnable(){

mit

service.execute(new Runnable(){

Ausgabe:

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)

Wie gehe ich mit solchen Szenarien um, wenn ich submit () verwende?

  1. Betten Sie Ihren Task-Code ( entweder ausführbare oder aufrufbare Implementierung) in den Blockcode try {} catch {} ein
  2. Implementieren CustomThreadPoolExecutor

Neue Lösung:

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

Ausgabe:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero

Gute klare Erklärung. Obwohl es NICHT wirklich erforderlich ist. Nur dieses zukünftige Objekt muss verbraucht werden, um zu wissen, ob die Aufgabe erfolgreich war oder nicht. Verwenden Sie daher
submit

11

Wenn Sie sich nicht für den Rückgabetyp interessieren, verwenden Sie execute. Es ist dasselbe wie Submit, nur ohne die Rückkehr von Future.


15
Dies ist gemäß der akzeptierten Antwort nicht korrekt. Die Ausnahmebehandlung ist ein ziemlich bedeutender Unterschied.
Zero3

7

Entnommen aus dem Javadoc:

Die Methode submiterweitert die Basismethode {@link Executor # execute}, indem sie eine {@link Future} erstellt und zurückgibt, mit der die Ausführung abgebrochen und / oder auf den Abschluss gewartet werden kann.

Persönlich bevorzuge ich die Verwendung von execute, weil es sich deklarativer anfühlt, obwohl dies wirklich eine Frage der persönlichen Präferenz ist.

Weitere Informationen: Im Fall der ExecutorServiceImplementierung ist die Kernimplementierung, die vom Aufruf an zurückgegeben Executors.newSingleThreadedExecutor()wird, a ThreadPoolExecutor.

Die submitAnrufe werden vom übergeordneten AbstractExecutorServiceAnruf bereitgestellt und alle Anrufe werden intern ausgeführt. Ausführen wird von der ThreadPoolExecutordirekt überschrieben / bereitgestellt .


2

Aus dem Javadoc :

Der Befehl kann nach Ermessen der Executor-Implementierung in einem neuen Thread, in einem Pool-Thread oder im aufrufenden Thread ausgeführt werden.

Abhängig von der Implementierung von können ExecutorSie feststellen, dass der sendende Thread blockiert, während die Aufgabe ausgeführt wird.


1

Die vollständige Antwort besteht aus zwei Antworten, die hier veröffentlicht wurden (plus ein bisschen "extra"):

  • Wenn Sie eine Aufgabe senden (oder ausführen), erhalten Sie eine Zukunft zurück, in der Sie das Ergebnis abrufen oder die Aktion abbrechen können. Sie haben diese Art der Kontrolle nicht, wenn Sie execute(weil seine Rückgabetyp-ID void)
  • executeerwartet, dass eine RunnableWeile submitentweder a Runnableoder a Callableals Argument nehmen kann (für weitere Informationen über den Unterschied zwischen den beiden - siehe unten).
  • executesprudelt keine unkontrollierte-Ausnahmen sofort (nicht überprüft Ausnahmen werfen kann !!!), während submitbindet jeder in die Zukunft Art der Ausnahme , dass die Renditen als Ergebnis, und nur , wenn Sie rufen future.get()die a (verpackt) Ausnahme ausgelöst wird. Das Throwable, das Sie erhalten, ist eine Instanz von. ExecutionExceptionWenn Sie dieses Objekt aufrufen getCause(), wird das ursprüngliche Throwable zurückgegeben.

Noch ein paar (verwandte) Punkte:

  • Auch wenn für die gewünschte Aufgabe submitkein Ergebnis zurückgegeben werden muss, können Sie sie weiterhin verwenden Callable<Void>(anstelle von a Runnable).
  • Das Abbrechen von Aufgaben kann über den Interrupt- Mechanismus erfolgen. Hier ist ein Beispiel für die Implementierung einer Stornierungsrichtlinie

Zusammenfassend ist es eine bessere Praxis, submitmit a Callable(vs. executemit a Runnable) zu arbeiten. Und ich zitiere aus "Java-Parallelität in der Praxis" von Brian Goetz:

6.3.2 Ergebnistragende Aufgaben: Callable und Future

Das Executor-Framework verwendet Runnable als grundlegende Darstellung der Aufgaben. Runnable ist eine ziemlich einschränkende Abstraktion. run kann keinen Wert zurückgeben oder geprüfte Ausnahmen auslösen, kann jedoch Nebenwirkungen haben, z. B. das Schreiben in eine Protokolldatei oder das Platzieren eines Ergebnisses in einer gemeinsam genutzten Datenstruktur. Viele Aufgaben sind effektiv verzögerte Berechnungen - Ausführen einer Datenbankabfrage, Abrufen einer Ressource über das Netzwerk oder Berechnen einer komplizierten Funktion. Für diese Aufgabentypen ist Callable eine bessere Abstraktion: Es wird erwartet, dass der Haupteinstiegspunkt call einen Wert zurückgibt und eine Ausnahme auslöst.7 Executors enthält verschiedene Dienstprogrammmethoden zum Umschließen anderer Aufgabentypen, einschließlich Runnable und java.security.PrivilegedAction mit einem Callable.


1

Einfach zur akzeptierten Antwort hinzufügen-

Von Aufgaben ausgelöste Ausnahmen gelangen jedoch nur für Aufgaben, die mit execute () übergeben wurden, in den nicht erfassten Ausnahmebehandler. Bei Aufgaben, die mit submit () an den Executor-Service gesendet wurden, wird jede ausgelöste Ausnahme als Teil des Rückgabestatus der Aufgabe betrachtet.

Quelle

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.