Angenommen, ich habe eine Warteschlange voller Aufgaben, die ich an einen Executor-Service senden muss. Ich möchte, dass sie einzeln verarbeitet werden. Der einfachste Weg, den ich mir vorstellen kann, ist:
- Nehmen Sie eine Aufgabe aus der Warteschlange
- Senden Sie es an den Testamentsvollstrecker
- Rufen Sie .get für die zurückgegebene Zukunft auf und blockieren Sie, bis ein Ergebnis verfügbar ist
- Nehmen Sie eine andere Aufgabe aus der Warteschlange ...
Ich versuche jedoch zu vermeiden, vollständig zu blockieren. Wenn ich 10.000 solcher Warteschlangen habe, deren Aufgaben einzeln verarbeitet werden müssen, geht mir der Stapelspeicher aus, da die meisten von ihnen an blockierten Threads festhalten.
Ich möchte eine Aufgabe einreichen und einen Rückruf bereitstellen, der aufgerufen wird, wenn die Aufgabe abgeschlossen ist. Ich werde diese Rückrufbenachrichtigung als Flag verwenden, um die nächste Aufgabe zu senden. (FunctionalJava und Jetlang verwenden anscheinend solche nicht blockierenden Algorithmen, aber ich kann ihren Code nicht verstehen.)
Wie kann ich das mit java.util.concurrent von JDK tun, ohne meinen eigenen Executor-Service zu schreiben?
(Die Warteschlange, die mich mit diesen Aufgaben versorgt, blockiert möglicherweise selbst, aber das ist ein Problem, das später behoben werden muss.)
Callback
Schnittstelle, die Sie deklarieren; nicht aus einer Bibliothek. Heute würde ich wahrscheinlich verwenden Sie einfachRunnable
,Consumer
oderBiConsumer
, je nachdem , was ich brauche von der Aufgabe auf den Hörer zurücklaufen.In Java 8 können Sie CompletableFuture verwenden . Hier ist ein Beispiel in meinem Code, in dem ich Benutzer aus meinem Benutzerdienst abrufe, sie meinen Ansichtsobjekten zuordne und dann meine Ansicht aktualisiere oder einen Fehlerdialog anzeige (dies ist eine GUI-Anwendung):
Es wird asynchron ausgeführt. Ich verwende zwei private Methoden:
mapUsersToUserViews
undupdateView
.quelle
Verwenden Sie die abhörbare zukünftige API von Guava und fügen Sie einen Rückruf hinzu. Vgl. von der Website:
quelle
Sie können die
FutureTask
Klasse erweitern, diedone()
Methode überschreiben und dann dasFutureTask
Objekt zum hinzufügenExecutorService
, sodass diedone()
MethodeFutureTask
sofort nach Abschluss der Methode aufgerufen wird .quelle
then add the FutureTask object to the ExecutorService
Könnten Sie mir bitte sagen, wie das geht?ThreadPoolExecutor
hat auchbeforeExecute
undafterExecute
Hook-Methoden, die Sie überschreiben und verwenden können. Hier ist die Beschreibung vonThreadPoolExecutor
's Javadocs .quelle
Verwenden Sie a
CountDownLatch
.Es ist von
java.util.concurrent
und es ist genau der Weg, um zu warten, bis mehrere Threads die Ausführung abgeschlossen haben, bevor Sie fortfahren.Um den gewünschten Rückrufeffekt zu erzielen, ist ein wenig zusätzliche Arbeit erforderlich. Wenn Sie dies in einem separaten Thread selbst erledigen, der das verwendet
CountDownLatch
und darauf wartet, werden Sie dann benachrichtigt, was auch immer Sie benachrichtigen müssen. Es gibt keine native Unterstützung für Rückrufe oder ähnliches.EDIT: Jetzt, wo ich Ihre Frage besser verstehe, denke ich, dass Sie unnötigerweise zu weit gehen. Wenn Sie einen regulären nehmen
SingleThreadExecutor
, geben Sie ihm alle Aufgaben, und es wird die Warteschlange nativ erledigen.quelle
Wenn Sie sicherstellen möchten, dass keine Aufgaben gleichzeitig ausgeführt werden, verwenden Sie einen SingleThreadedExecutor . Die Aufgaben werden in der Reihenfolge bearbeitet, in der sie eingereicht wurden. Sie müssen die Aufgaben nicht einmal halten, sondern sie nur an den Exec senden.
quelle
Einfacher Code zur Implementierung des
Callback
Mechanismus mitExecutorService
Ausgabe:
Wichtige Hinweise:
newFixedThreadPool(5)
durchnewFixedThreadPool(1)
Wenn Sie die nächste Aufgabe nach der Analyse des Ergebnisses
callback
der vorherigen Aufgabe bearbeiten möchten, entfernen Sie einfach den Kommentar unter der ZeileSie können durch
newFixedThreadPool()
eines von ersetzenabhängig von Ihrem Anwendungsfall.
Wenn Sie die Rückrufmethode asynchron behandeln möchten
ein.
ExecutorService or ThreadPoolExecutor
Übergeben Sie eine freigegebene an Callable-Aufgabeb. Konvertieren Sie Ihre
Callable
Methode in eineCallable/Runnable
Aufgabec. Rückrufaufgabe auf drücken
ExecutorService or ThreadPoolExecutor
quelle
Nur um Matts Antwort zu ergänzen, die geholfen hat, hier ein ausführlicheres Beispiel, das die Verwendung eines Rückrufs zeigt.
Die Ausgabe ist:
quelle
Sie können eine Implementierung von Callable so verwenden, dass
wo CallbackInterface etwas sehr Grundlegendes ist
und jetzt wird die Hauptklasse so aussehen
quelle
Dies ist eine Erweiterung von Paches Antwort mit Guavas
ListenableFuture
.Insbesondere können
Futures.transform()
RückgabenListenableFuture
verwendet werden, um asynchrone Aufrufe zu verketten.Futures.addCallback()
gibt zurückvoid
, kann also nicht zum Verketten verwendet werden, ist aber gut für die Behandlung von Erfolg / Misserfolg bei einem asynchronen Abschluss.ANMERKUNG: Zusätzlich zur Verkettung asynchroner Aufgaben
Futures.transform()
können Sie jede Aufgabe auf einem separaten Executor planen (in diesem Beispiel nicht gezeigt).quelle