Zdefiniuj interfejs wywołania zwrotnego, aby otrzymywać wszelkie parametry, które chcesz przekazać w powiadomieniu o zakończeniu. Następnie wywołaj go na końcu zadania.
Możesz nawet napisać ogólne opakowanie dla zadań Runnable i przesłać je do ExecutorService
. Lub zobacz poniżej mechanizm wbudowany w Javę 8.
class CallbackTask implements Runnable {
private final Runnable task;
private final Callback callback;
CallbackTask(Runnable task, Callback callback) {
this.task = task;
this.callback = callback;
}
public void run() {
task.run();
callback.complete();
}
}
Z CompletableFuture
Java 8 zawarte dokładniejszym środki komponować rurociągów gdzie procesy mogą być zrealizowanych asynchronicznie i warunkowo. Oto wymyślony, ale kompletny przykład powiadomienia.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class GetTaskNotificationWithoutBlocking {
public static void main(String... argv) throws Exception {
ExampleService svc = new ExampleService();
GetTaskNotificationWithoutBlocking listener = new GetTaskNotificationWithoutBlocking();
CompletableFuture<String> f = CompletableFuture.supplyAsync(svc::work);
f.thenAccept(listener::notify);
System.out.println("Exiting main()");
}
void notify(String msg) {
System.out.println("Received message: " + msg);
}
}
class ExampleService {
String work() {
sleep(7000, TimeUnit.MILLISECONDS); /* Pretend to be busy... */
char[] str = new char[5];
ThreadLocalRandom current = ThreadLocalRandom.current();
for (int idx = 0; idx < str.length; ++idx)
str[idx] = (char) ('A' + current.nextInt(26));
String msg = new String(str);
System.out.println("Generated message: " + msg);
return msg;
}
public static void sleep(long average, TimeUnit unit) {
String name = Thread.currentThread().getName();
long timeout = Math.min(exponential(average), Math.multiplyExact(10, average));
System.out.printf("%s sleeping %d %s...%n", name, timeout, unit);
try {
unit.sleep(timeout);
System.out.println(name + " awoke.");
} catch (InterruptedException abort) {
Thread.currentThread().interrupt();
System.out.println(name + " interrupted.");
}
}
public static long exponential(long avg) {
return (long) (avg * -Math.log(1 - ThreadLocalRandom.current().nextDouble()));
}
}
Callback
interfejs, który deklarujesz; nie z biblioteki. Obecnie I pewnie po prostu użyćRunnable
,Consumer
lubBiConsumer
, w zależności od tego, co muszę przejść z powrotem od zadania do słuchacza.W Javie 8 możesz użyć CompletableFuture . Oto przykład, który miałem w moim kodzie, w którym używam go do pobierania użytkowników z mojej usługi użytkownika, mapowania ich na obiekty widoku, a następnie aktualizowania widoku lub wyświetlania okna dialogowego błędu (jest to aplikacja GUI):
Wykonuje się asynchronicznie. Używam dwóch prywatnych metod:
mapUsersToUserViews
iupdateView
.źródło
Użyj przyszłego API, które można nasłuchiwać, i dodaj wywołanie zwrotne. Por. ze strony internetowej:
źródło
Możesz rozszerzyć
FutureTask
klasę i przesłonićdone()
metodę, a następnie dodaćFutureTask
obiekt do klasyExecutorService
, dzięki czemudone()
metoda zostanie wywołanaFutureTask
natychmiast po zakończeniu.źródło
then add the FutureTask object to the ExecutorService
, czy mógłbyś mi powiedzieć, jak to zrobić?ThreadPoolExecutor
ma równieżbeforeExecute
iafterExecute
metody przechwytywania, które można przesłonić i wykorzystać. Oto opis odThreadPoolExecutor
„s Javadocs .źródło
Użyj
CountDownLatch
.To jest
java.util.concurrent
dokładnie sposób, w jaki należy czekać na zakończenie wykonywania kilku wątków przed kontynuowaniem.Aby uzyskać efekt oddzwaniania, którym się opiekujesz, wymaga to trochę dodatkowej pracy. Mianowicie załatwienie tego samodzielnie w osobnym wątku, który używa
CountDownLatch
i czeka na to, a następnie powiadamia o tym, co chcesz powiadomić. Nie ma natywnej obsługi wywołań zwrotnych ani niczego podobnego do tego efektu.EDYCJA: teraz, kiedy lepiej rozumiem twoje pytanie, myślę, że posuwasz się za daleko, niepotrzebnie. Jeśli bierzesz zwykły
SingleThreadExecutor
, daj mu wszystkie zadania, a kolejkowanie wykona natywnie.źródło
Jeśli chcesz mieć pewność, że żadne zadania nie będą uruchamiane w tym samym czasie, użyj SingleThreadedExecutor . Zadania będą realizowane w kolejności, w jakiej zostały przesłane. Nie musisz nawet wstrzymywać zadań, po prostu prześlij je do exec.
źródło
Prosty kod do implementacji
Callback
mechanizmu przy użyciuExecutorService
wynik:
Kluczowe uwagi:
newFixedThreadPool(5)
znewFixedThreadPool(1)
Jeśli chcesz przetworzyć następne zadanie po przeanalizowaniu wyniku
callback
poprzedniego zadania, po prostu odznacz poniżej linięMożesz zastąpić
newFixedThreadPool()
jednym zw zależności od przypadku użycia.
Jeśli chcesz asynchronicznie obsługiwać metodę wywołania zwrotnego
za. Przekaż
ExecutorService or ThreadPoolExecutor
zadanie udostępnione do wywoływanegob. Zamień swoją
Callable
metodę naCallable/Runnable
zadaniedo. Przekaż zadanie zwrotne do
ExecutorService or ThreadPoolExecutor
źródło
Aby dodać do odpowiedzi Matta, która pomogła, oto bardziej szczegółowy przykład pokazujący użycie wywołania zwrotnego.
Wynik to:
źródło
Możesz użyć implementacji wywoływanej, takiej że
gdzie CallbackInterface jest czymś bardzo podstawowym, jak
a teraz główna klasa będzie wyglądać tak
źródło
To jest rozszerzenie odpowiedzi Pache'a używającej guawy
ListenableFuture
.W szczególności
Futures.transform()
zwracaListenableFuture
tak, może być używany do łączenia wywołań asynchronicznych.Futures.addCallback()
zwracavoid
, więc nie może być używany do łączenia w łańcuch, ale jest dobry do obsługi sukcesu / niepowodzenia w przypadku zakończenia asynchronicznego.UWAGA: Oprócz łączenia zadań asynchronicznych w łańcuch
Futures.transform()
umożliwia również zaplanowanie każdego zadania w osobnym module wykonawczym (nie pokazano w tym przykładzie).źródło