Java 5:java.util.concurrent.FutureTask – Semantik von cancel() und done()
-
22-07-2019 - |
Frage
Ich bin derzeit auf der Suche nach einem bösen Fehler in einer Multithread-Umgebung, die FutureTasks und Executors verwendet.Die Grundidee besteht darin, eine feste Anzahl von Threads einzelne FutureTasks ausführen zu lassen, die ein Ergebnis berechnen, das in einer Tabelle angezeigt werden soll (ganz zu schweigen vom GUI-Aspekt).
Ich habe mir das schon so lange angesehen, dass ich langsam an meinem Verstand zweifele.
Betrachten Sie diesen Code:
public class MyTask extends FutureTask<Result> {
private String cellId;
...
protected void done() {
if (isCancelled()) return;
try {
Result r = get(); // should not wait, because we are done
... // some processing with r
sendMessage(cellId, r);
} catch (ExecutionException e) { // thrown from get
...
} catch (InterruptedException e) { // thrown from get
...
}
}
...
}
Wann done()
von einem Executor aufgerufen wird, der eine Instanz von MyTask verarbeitet, überprüfe ich, ob ich dort angekommen bin, da die Aufgabe abgebrochen wurde.Wenn ja, überspringe ich alle verbleibenden Aktivitäten, insbesondere rufe ich nicht an sendMessage()
.
In der Dokumentation zu FutureTask.done() heißt es:
Geschützte Methode, die aufgerufen wird, wenn diese Aufgabe in den Status isDone übergeht (ob normal oder durch Abbruch).Die Standardimplementierung führt nichts aus.Unterklassen können diese Methode überschreiben, um Abschlussrückrufe aufzurufen oder die Buchhaltung durchzuführen.Beachten Sie, dass Sie den Status innerhalb der Implementierung dieser Methode abfragen können, um festzustellen, ob diese Aufgabe abgebrochen wurde.(API-Referenz)
Was ich aber aus der Dokumentation nicht verstehe FutureTask
sind die Semantik während done()
wird ausgeführt.Was ist, wenn ich das bestehe isCancelled()
Überprüfen Sie es am Anfang, aber gleich danach ruft ein anderer Thread meine an cancel()
Methode?Wird das dazu führen, dass meine Aufgabe ihre Meinung und Antwort ändert? isCancelled() == true
von da an?
Wenn ja, woher weiß ich später, ob die Nachricht gesendet wurde?Anschauen isDone()
Ich würde mir nur sagen, dass die Ausführung der Aufgabe abgeschlossen ist, aber wie isCancelled()
damals auch wahr waren, konnte ich nicht wissen, ob es die Nachricht noch rechtzeitig verschicken konnte.
Vielleicht ist das offensichtlich, aber ich sehe es im Moment nicht wirklich.
Lösung
Aus der API (Hervorhebung von mir):
öffentlicher boolescher Abbruch (boolean mayInterruptIfRunning)
Beschreibung von der Schnittstelle kopiert:Zukunft
Versucht, die Ausführung dieser Aufgabe abzubrechen. Dieser Versuch schlägt fehl, wenn die Aufgabe bereits abgeschlossen wurde, bereits storniert wurde oder aus einem anderen Grund nicht storniert werden konnte.
FutureTask geht also davon aus, dass Sie eine Aufgabe nicht abbrechen können, wenn sie in die Phase „isDone“ übergegangen ist.
Andere Tipps
FutureTask#done()
heißt nicht mehr als einmal für jede gegebene Beispiel, und es ist nur aus einem Grund genannt - run()
abgeschlossen entweder mit oder ohne Fehler oder cancel()
lief vor einem der vorhergehenden Ereignisse aufgetreten sind. Die Aufzeichnung der Fertigstellung durch eines dieser Ergebnisse ist Rast . Der Grund, ein FutureTask
abgeschlossen nicht ändern kann, und zwar unabhängig von Ereignissen scheinbar geschehen im Wettbewerb „zugleich.“
Daher innerhalb FutureTask#done()
nur eine von isCancelled()
oder isDone()
wird return true dann und immer mehr. Es ist schwierig, zwischen isDone()
Berichterstattung wahr haft Fehlern oder erfolgreichen Abschluss zu unterscheiden. Sie können nicht set()
oder setException(Throwable)
außer Kraft entscheidend, sowohl als Delegierter an den inneren AQS
, um zu entscheiden, ob der Versuch ein erfolgreiches Nachgeben eines Wertes zu erfassen oder eine Ausnahme zu stoßen sollte bleiben. nur entweder Methode überschrieben lassen Sie wissen, dass es genannt wurde, aber man kann die Entscheidung der Basisimplementierung nicht beobachten. Wenn entweder Ereignis eintritt „zu spät“ -Say, nach Storno der Versuch, den Wert oder die Ausnahme zu erfassen, werden ignoriert.
Das Studium der Implementierung, der einzige Weg, ich sehe eine nicht abgebrochen erfolgreiches Ergebnis zu unterscheiden von einem Fehler den sauren Apfel beißen und get()
nennen.
Warum nicht die Meldung "außerhalb" der Aufgabe senden, basierend auf dem Ergebnis der Future
Wenn Sie diesen Ansatz nehmen, sollten Sie zwei separate verwenden ExecutorService Instanzen: eine für die Hauptaufgaben und eine für die Sekundär diejenigen. Dies ist zu Deadlocks zu verhindern. Sie haben keine Nebenaufgaben beginnen sollen und möglicherweise primäre Aufgaben blockieren anspringt, wenn die Threadpoolgröße ist begrenzt.
Es gibt keine Notwendigkeit, FutureTask
Ich schlage vor, einen kleinen Testfall zu schreiben, die Sie cancel()
während Future
Instanz hängt in done()
aufrufen können und sehen, was passiert.