Jaki jest najprostszy sposób oczekiwania na ExecutorService
zakończenie wszystkich zadań ? Moje zadanie jest głównie obliczeniowe, więc chcę po prostu uruchomić dużą liczbę zadań - po jednym na każdym rdzeniu. W tej chwili moja konfiguracja wygląda następująco:
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
implementuje runnable. Wygląda na to, że poprawnie wykonuje zadania, ale kod zawiesza się wait()
przy IllegalMonitorStateException
. To dziwne, bo bawiłem się przykładami zabawek i wyglądało na to, że działają.
uniquePhrases
zawiera kilkadziesiąt tysięcy elementów. Czy powinienem używać innej metody? Szukam czegoś tak prostego, jak to możliwe
java
multithreading
threadpool
executorservice
George Smiley
źródło
źródło
es
), gdy chcesz na niego poczekać - blokada zostanie automatycznie zwolniona podczas oczekiwaniaExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());
Odpowiedzi:
Najprostszym podejściem jest użycie,
ExecutorService.invokeAll()
które robi to, co chcesz w jednej linijce. W swoim języku musisz zmodyfikować lub zawinąć,ComputeDTask
aby zaimplementowaćCallable<>
, co może dać ci nieco większą elastyczność. Prawdopodobnie w Twojej aplikacji jest znacząca implementacjaCallable.call()
, ale oto sposób na jej zawinięcie, jeśli nie jest używanyExecutors.callable()
.Jak zauważyli inni, w
invokeAll()
razie potrzeby możesz użyć wersji limitu czasu . W tym przykładzieanswers
będzie zawierać wiązkęFuture
s, która zwróci null (patrz definicjaExecutors.callable()
. Prawdopodobnie to, co chcesz zrobić, to niewielkie przeredagowanie, abyś mógł uzyskać przydatną odpowiedź lub odniesienie do instrumentu bazowegoComputeDTask
, ale ja mogę nie mów z twojego przykładu.Jeśli nie jest to jasne, pamiętaj, że
invokeAll()
nie powróci, dopóki wszystkie zadania nie zostaną zakończone. (tzn. wszystkieFuture
s w twojejanswers
kolekcji zostaną zgłoszone,.isDone()
jeśli zostaniesz o to poproszony). Pozwala to uniknąć ręcznego wyłączania, oczekiwania na zakończenie itp. i pozwala ci to wykorzystać ponownieExecutorService
starannie dla wielu cykli, jeśli chcesz.Istnieje kilka powiązanych pytań dotyczących SO:
Jak czekać na zakończenie wszystkich wątków
Zwraca wartości z wątków Java
invokeAll () nie chce przyjąć kolekcji <Callable <t>>
Czy muszę synchronizować?
Żadne z nich nie jest ściśle związane z twoim pytaniem, ale zapewniają odrobinę koloru na temat tego, jak ludzie myślą
Executor
/ExecutorService
powinni być wykorzystywani.źródło
Jeśli chcesz poczekać na zakończenie wszystkich zadań, użyj
shutdown
metody zamiastwait
. Następnie postępuj zgodnie zawaitTermination
.Ponadto można użyć
Runtime.availableProcessors
do uzyskania liczby wątków sprzętowych, aby można było poprawnie zainicjować pulę wątków.źródło
awaitTermination
wymaga limitu czasu jako parametru. Chociaż można podać skończony czas i umieścić wokół niego pętlę, aby poczekać, aż wszystkie wątki się zakończą, zastanawiałem się, czy istnieje bardziej eleganckie rozwiązanie.Jeśli czekanie na
ExecutorService
zakończenie wszystkich zadań nie jest dokładnie twoim celem, ale raczej czekanie, aż konkretna partia zadań zostanie zakończona, możesz użyćCompletionService
- w szczególnościExecutorCompletionService
.Chodzi o to, aby stworzyć
ExecutorCompletionService
owijania TWOJEJExecutor
, złożyć jakąś znaną liczbę zadań poprzezCompletionService
, po czym wyciągnąć tę samą liczbę wyników z kolejki realizacji przy użyciu albotake()
(co bloki) lubpoll()
(co nie). Po narysowaniu wszystkich oczekiwanych wyników odpowiadających przesłanym zadaniom wiesz, że wszystkie zostały wykonane.Pozwól mi powiedzieć to jeszcze raz, ponieważ nie jest to oczywiste z interfejsu: musisz wiedzieć, ile rzeczy wkładasz do
CompletionService
, aby wiedzieć, ile rzeczy spróbować wyciągnąć. Ma to szczególne znaczenie w przypadkutake()
metody: wywołaj ją o jeden raz za dużo, a ona zablokuje Twój wątek wywołujący, dopóki jakiś inny wątek nie prześle innego zadania do tego samegoCompletionService
.Istnieje kilka przykładów pokazujących, jak korzystać
CompletionService
z książki Java Concurrency in Practice .źródło
CompletionService
Jeśli chcesz poczekać, aż usługa modułu wykonującego zakończy działanie, zadzwoń,
shutdown()
a następnie poczekaj na terminację (jednostki, typ jednostki) , npawaitTermination(1, MINUTE)
. ExecutorService nie blokuje na swoim własnym monitorze, więc nie można używaćwait
itp.źródło
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
stackoverflow.com/a/1250655/32453Możesz czekać na zakończenie zadań w określonym przedziale czasu:
Lub możesz użyć ExecutorService . przesłać ( Runnable ) i zebrać przyszłe obiekty, które zwraca, i wywołać get () po kolei, aby czekać na zakończenie.
InterruptedException jest niezwykle ważny, aby poprawnie się z nim obchodzić. To właśnie pozwala tobie lub użytkownikom twojej biblioteki bezpiecznie zakończyć długi proces.
źródło
Po prostu użyj
W każdym wątku
i jako bariera
źródło
Główna przyczyna wyjątku IllegalMonitorStateException :
Z twojego kodu właśnie wywołałeś wait () na ExecutorService bez posiadania blokady.
Poniższy kod naprawi
IllegalMonitorStateException
Wykonaj jedno z poniższych podejść, aby poczekać na zakończenie wszystkich zadań, które zostały przesłane
ExecutorService
.Iterację wszystkich
Future
zadań zsubmit
naExecutorService
i sprawdzeniu stanu z blokowania połączeńget()
naFuture
obiekcieKorzystanie z invokeAll on
ExecutorService
Korzystanie z CountDownLatch
Korzystanie z ForkJoinPool lub newWorkStealingPool of
Executors
(od java 8)Zamknij pulę zgodnie z zaleceniami na stronie dokumentacji Oracle
Jeśli chcesz z wdziękiem czekać na zakończenie wszystkich zadań, gdy używasz opcji 5 zamiast opcji 1 do 4, zmień
do
while(condition)
który sprawdza co 1 minutę.źródło
Możesz użyć
ExecutorService.invokeAll
metody, wykona wszystkie zadania i poczeka, aż wszystkie wątki zakończą swoje zadanie.Oto pełna wersja javadoc
Możesz także przeciążoną przez użytkownika wersję tej metody, aby określić limit czasu.
Oto przykładowy kod z
ExecutorService.invokeAll
źródło
Mam również sytuację, że mam zestaw dokumentów do przeszukania. Zaczynam od początkowego dokumentu „źródłowego”, który powinien zostać przetworzony, dokument ten zawiera linki do innych dokumentów, które również powinny zostać przetworzone, i tak dalej.
W moim głównym programie chcę po prostu napisać coś takiego, w którym
Crawler
kontroluje kilka wątków.Taka sama sytuacja miałaby miejsce, gdybym chciał nawigować po drzewie; wskoczyłbym do węzła głównego, procesor dla każdego węzła dodawałby dzieci do kolejki w razie potrzeby, a wiązka wątków przetwarzałaby wszystkie węzły w drzewie, dopóki nie będzie już więcej.
W JVM nie mogłem znaleźć niczego, co moim zdaniem było nieco zaskakujące. Napisałem więc klasę,
ThreadPool
której można użyć bezpośrednio lub podklasy, aby dodać metody odpowiednie dla domeny, npschedule(Document)
. Mam nadzieję, że to pomoże!ThreadPool Javadoc | Maven
źródło
Dodaj wszystkie wątki w kolekcji i prześlij za pomocą
invokeAll
. Jeśli możesz użyćinvokeAll
metodyExecutorService
, JVM nie przejdzie do następnego wiersza, dopóki wszystkie wątki nie zostaną zakończone.Oto dobry przykład: invokeAll za pośrednictwem ExecutorService
źródło
Prześlij swoje zadania do Runnera, a następnie poczekaj na wywołanie metody waitTillDone () w następujący sposób:
Aby go użyć, dodaj następującą zależność grad / maven:
'com.github.matejtymes:javafixes:1.0'
Aby uzyskać więcej informacji, spójrz tutaj: https://github.com/MatejTymes/JavaFixes lub tutaj: http://matejtymes.blogspot.com/2016/04/executor-that-notified-you-when-task.html
źródło
Prostą alternatywą jest użycie wątków wraz z łączeniem. Patrz: łączenie wątków
źródło
Poczekam tylko na zakończenie executora z określonym limitem czasu, który Twoim zdaniem jest odpowiedni do wykonania zadań.
źródło
Wygląda na to, że potrzebujesz
ForkJoinPool
i użyj globalnej puli do wykonywania zadań.Piękno polega na
pool.awaitQuiescence
tym, że metoda będzie blokować wykorzystanie wątku dzwoniącego do wykonywania swoich zadań, a następnie powrót, gdy będzie naprawdę pusty.źródło