Mam metodę, która zwraca wartość List
przyszłości
List<Future<O>> futures = getFutures();
Teraz chcę poczekać, aż wszystkie futures zostaną pomyślnie przetworzone lub którekolwiek z zadań, których dane wyjściowe zostaną zwrócone przez przyszłość, zgłosi wyjątek. Nawet jeśli jedno zadanie rzuca wyjątek, nie ma sensu czekać na inną przyszłość.
Byłoby proste podejście
wait() {
For(Future f : futures) {
try {
f.get();
} catch(Exception e) {
//TODO catch specific exception
// this future threw exception , means somone could not do its task
return;
}
}
}
Ale problem polega na tym, że jeśli, na przykład, czwarta przyszłość generuje wyjątek, wtedy będę niepotrzebnie czekać, aż pierwsze 3 futures będą dostępne.
Jak to rozwiązać? Czy w jakikolwiek sposób odlicza przyswajanie? Nie mogę użyć Future, isDone
ponieważ dokument java mówi
boolean isDone()
Returns true if this task completed. Completion may be due to normal termination, an exception, or cancellation -- in all of these cases, this method will return true.
java
multithreading
future
user93796
źródło
źródło
ExecutionService
dla każdej „partii” zadań, przesłać je do niej, a następnie natychmiast zamknąć usługę iawaitTermination()
jak przypuszczam użyć na niej.CountDownLatch
jeśli owinąłeś ciało wszystkich swoich przyszłości w a,try..finally
aby upewnić się, że zatrzask również zostanie zmniejszony.Odpowiedzi:
Możesz użyć CompletionService, aby otrzymać kontrakty futures, gdy tylko będą gotowe, a jeśli jeden z nich zgłosi wyjątek, anuluj przetwarzanie. Coś takiego:
Myślę, że możesz dalej ulepszyć, aby anulować wszystkie nadal wykonywane zadania, jeśli jedno z nich zgłosi błąd.
źródło
CompletionService
.Jeśli używasz Java 8, możesz to zrobić łatwiej dzięki CompletableFuture i CompletableFuture.allOf , które stosują wywołanie zwrotne dopiero po wykonaniu wszystkich dostarczonych CompletableFutures.
źródło
Future
instancje, nie możesz zastosować tej metody. Nie jest łatwo przekonwertowaćFuture
naCompletableFuture
.Użyj a
CompletableFuture
w Javie 8źródło
Możesz użyć ExecutorCompletionService . Dokumentacja zawiera nawet przykład dokładnego przypadku użycia:
Ważną rzeczą, na którą należy zwrócić uwagę, jest to, że ecs.take () otrzyma pierwsze ukończone zadanie, a nie tylko pierwsze przesłane. Dlatego powinieneś je otrzymywać w kolejności kończenia egzekucji (lub rzucania wyjątku).
źródło
Jeśli używasz Java 8 i nie chcesz manipulować
CompletableFuture
s, napisałem narzędzie do pobierania wyników dlaList<Future<T>>
korzystania z przesyłania strumieniowego. Kluczem jest to, że nie wolno cimap(Future::get)
rzucać.To wymaga narzędzia,
AggregateException
które działa jak C #Ten składnik działa dokładnie tak samo, jak Task.WaitAll języka C # . Pracuję nad wariantem, który robi to samo co
CompletableFuture.allOf
(odpowiednikTask.WhenAll
)Powodem, dla którego to zrobiłem, jest to, że używam Springa
ListenableFuture
i nie chcę portować,CompletableFuture
mimo że jest to bardziej standardowy sposóbźródło
W przypadku, gdy chcesz połączyć listę możliwych do zrealizowania przyszłości, możesz to zrobić:
Aby uzyskać więcej informacji na temat Future & CompletableFuture, przydatne linki:
1. Future: https://www.baeldung.com/java-future
2. CompletableFuture: https://www.baeldung.com/java-completablefuture
3. CompletableFuture: https : //www.callicoder.com/java-8-completablefuture-tutorial/
źródło
może to by pomogło (nic nie zastąpiłoby nieprzetworzonego wątku, tak!) Proponuję uruchomić każdego
Future
gościa z oddzielnym wątkiem (idą równolegle), a potem, gdy kiedykolwiek pojawi się jeden z otrzymanych błędów, to po prostu zasygnalizuje menedżerowi (Handler
klasie).Muszę powiedzieć, że powyższy kod powodowałby błąd (nie sprawdzał), ale mam nadzieję, że mógłbym wyjaśnić rozwiązanie. proszę spróbować.
źródło
źródło
CompletionService pobierze Twoje Callables za pomocą metody .submit () i możesz pobrać obliczone futures za pomocą metody .take ().
Jedną rzeczą, o której nie można zapomnieć, jest zakończenie ExecutorService przez wywołanie metody .shutdown (). Możesz również wywołać tę metodę tylko wtedy, gdy zapisałeś odwołanie do usługi executora, więc upewnij się, że ją zachowujesz.
Przykładowy kod - dla stałej liczby elementów pracy, nad którymi należy pracować równolegle:
Przykładowy kod - dla dynamicznej liczby elementów pracy, nad którymi należy pracować równolegle:
źródło
Mam klasę narzędziową, która zawiera:
Gdy już to zrobisz, używając importu statycznego, możesz po prostu poczekać na wszystkie futures, takie jak ta:
możesz także zebrać wszystkie ich wyniki w ten sposób:
Po prostu wracam do mojego starego postu i zauważam, że miałeś kolejny żal:
W takim przypadku prostym rozwiązaniem jest zrobienie tego równolegle:
W ten sposób pierwszy wyjątek, chociaż nie zatrzyma przyszłości, złamie instrukcję forEach, jak w przykładzie seryjnym, ale ponieważ wszystkie czekają równolegle, nie będziesz musiał czekać na zakończenie pierwszych 3.
źródło
źródło