Ich lerne, ExectorService
zu verwenden, um threads
zu bündeln und Aufgaben zu versenden. Ich habe unten ein einfaches Programm
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());
}
}
}
Es ist nichts Besonderes, erstellt jedoch zwei threads
und übermittelt insgesamt 5 Aufgaben. Nachdem jeder thread
seine Aufgabe abgeschlossen hat, wird der nächste, Verwendet. Im obigen Code verwende ich executor.submit
. Ich habe auch zu executor.execute
gewechselt. Ich sehe aber keinen Unterschied in der Ausgabe. Inwiefern unterscheiden sich die submit and execute
-Methoden? Das sagt die API
Die Methode submit erweitert die Basismethode Executor.execute (Java.lang.Runnable), indem eine Future erstellt und zurückgegeben wird, mit der die Ausführung abgebrochen und/oder auf den Abschluss gewartet werden kann. Die Methoden invokeAny und invokeAll führen die gebräuchlichsten Formen der Massenausführung aus, führen eine Auflistung von Aufgaben durch und warten, bis mindestens eine oder alle Aufgaben abgeschlossen sind. (Mit der Klasse ExecutorCompletionService können benutzerdefinierte Varianten dieser Methoden geschrieben werden.)
Aber es ist mir nicht klar, was es genau bedeutet?
Wie Sie aus dem JavaDoc sehen, gibt execute(Runnable)
nichts zurück.
submit(Callable<T>)
gibt jedoch ein Future
-Objekt zurück, mit dem Sie den laufenden Thread später programmatisch abbrechen und die T
abrufen können, die zurückgegeben wird, wenn die Callable
abgeschlossen ist. Siehe JavaDoc of Future für weitere Details
Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);
Wenn Wenn future.get() == null
und keine Ausnahme auslöst, wurde Runnable erfolgreich ausgeführt
Der Unterschied ist, dass execute
einfach die Task ohne weiteres startet, während submit
ein Future
-Objekt zur Verwaltung der Task zurückgibt. Mit dem Objekt Future
können Sie Folgendes tun:
cancel
vorzeitig ab.get
, bis die Task abgeschlossen ist.Die Future
-Schnittstelle ist nützlicher, wenn Sie eine Callable
an den Pool übergeben. Der Rückgabewert der call
-Methode wird zurückgegeben, wenn Sie Future.get
aufrufen. Wenn Sie keinen Verweis auf die Future
pflegen, besteht kein Unterschied.
execute:
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ür Future
zu ergreifen, die vom Aufruf zurückgegeben wurden
Hauptunterschied: Exception
Handhabung
submit()
verbirgt nicht behandelte Exception
im Framework selbst.
execute()
wirft Exception
nicht behandelt.
Lösung für die Behandlung von Ausnahmen mit submit()
Wickeln Sie Ihren Callable or Runnable code in try{} catch{} block
ODER
Behalte future.get() call in try{} catch{} block
ODER
eigene ThreadPoolExecutor
implementieren und afterExecute
-Methode überschreiben
Bezüglich Tour andere Fragen an
Führt die angegebenen Aufgaben aus und gibt eine Liste der Futures zurück, die ihren Status und ihre Ergebnisse behalten, wenn alle abgeschlossen sind oder das Timeout abgelaufen ist, je nachdem, was zuerst eintritt.
Führt die angegebenen Aufgaben aus und gibt das Ergebnis einer erfolgreich abgeschlossenen Task zurück (d. H. Ohne eine Ausnahme auszulösen), wenn dies vor Ablauf der angegebenen Zeitüberschreitung erledigt ist.
Verwenden Sie invokeAll
, wenn Sie warten möchten, bis alle übergebenen Aufgaben abgeschlossen sind.
Verwenden Sie invokeAny
, wenn Sie eine Aufgabe aus N übergebenen Aufgaben erfolgreich abschließen möchten. In diesem Fall werden laufende Aufgaben abgebrochen, wenn eine der Aufgaben erfolgreich abgeschlossen wurde.
Zugehöriger Beitrag mit Code-Beispiel:
Wählen Sie zwischen dem ExecutorService-Senden und dem ExecutorService-Ausführen
Senden - Gibt ein Future-Objekt zurück, mit dem das Ergebnis der übergebenen Aufgabe überprüft werden kann. Kann zum Abbrechen oder zum Überprüfen von isDone usw. verwendet werden.
Ausführen - gibt nichts zurück.
Ein Hauptunterschied zwischen der Methode submit () und der Methode execute () besteht darin, dass ExecuterService.submit () das Ergebnis der Berechnung zurückgeben kann, da es einen Rückgabetyp von Future gibt. Die Methode Ausführen () kann jedoch nichts zurückgeben, da der Rückgabetyp ungültig ist. Die Kernschnittstelle im Executor-Framework von Java 1.5 ist die Executor-Schnittstelle, die die Ausführungsmethode (Runnable Task) definiert, deren Hauptzweck es ist, die Task von ihrer Ausführung zu trennen.
Jede an Executor übergebene Aufgabe kann von demselben Thread, einem Arbeitsthread aus einem Threadpool oder einem anderen Thread ausgeführt werden.
Auf der anderen Seite ist die submit () -Methode in der ExecutorService-Schnittstelle definiert, die eine Unterschnittstelle von Executor ist und die Funktionalität zum Beenden des Thread-Pools sowie das Hinzufügen der submit () - Methode hinzufügt, die eine Callable-Task annehmen und ein Ergebnis zurückgeben kann der Berechnung.
Ähnlichkeiten zwischen dem Ausführen () und Senden () sowie:
Abgesehen von der Tatsache, dass die submit () -Methode die Ausgabe zurückgeben kann und execute () nicht kann, werden im Folgenden weitere Unterschiede zwischen diesen beiden Schlüsselmethoden des Executor-Frameworks von Java 5 aufgeführt.
Wenn Sie den Quellcode überprüfen, werden Sie feststellen, dass submit
eine Art Wrapper für execute
ist.
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
grundsätzlich werden beide Aufrufe ausgeführt, wenn Sie ein zukünftiges Objekt wünschen, rufen Sie die submit () -Methode .__ auf. hier vom 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;
}
wie Sie sehen, hat Java wirklich keine Möglichkeit, einen anderen Thread als den Aufruf der run () - Methode IMO zu starten. da ich auch festgestellt habe, dass die Callable.call()
-Methode innerhalb der run()
-Methode aufgerufen wird. Wenn das Objekt also aufrufbar ist, würde es noch die run()
-Methode aufrufen, die inturn die call()
-Methode von doc aus aufrufen würde.
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);
}
}