Muszę wykonać pewną liczbę zadań 4 na raz, mniej więcej tak:
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
//...wait for completion somehow
Jak mogę otrzymać powiadomienie, gdy wszystkie zostaną ukończone? Na razie nie mogę wymyślić nic lepszego niż ustawienie globalnego licznika zadań i zmniejszenie go na końcu każdego zadania, a następnie monitorowanie w nieskończonej pętli tego licznika, aby stał się 0; lub uzyskaj listę kontraktów futures i w nieskończonej pętli monitor jest zrobiony dla nich wszystkich. Jakie są lepsze rozwiązania nieobejmujące nieskończonych pętli?
Dzięki.
Long.MAX_VALUE, TimeUnit.NANOSECONDS
równoważnej z brakiem limitu czasu.java.util.concurrent
pakietu w sekcji: Aby poczekać „na zawsze”, możesz użyć wartościTiming
Long.MAX_VALUE
Użyj CountDownLatch :
i w ramach twojego zadania (załącz w try / wreszcie)
źródło
ExecutorService.invokeAll()
robi to dla ciebie.źródło
futures
zostaną zwrócone, zadania nie zostały zakończone. Mogą one zostać ukończone w przyszłości, a otrzymasz link do wyniku. Dlatego nazywa się toFuture
. Masz metodę Future.get () , która będzie czekać na zakończenie zadania, aby uzyskać wynik.Możesz również korzystać z list kontraktów terminowych:
wtedy, gdy chcesz dołączyć do nich wszystkich, jest to w zasadzie odpowiednik łączenia się na każdym z nich (z tą dodatkową korzyścią, że podnosi wyjątki od wątków potomnych do głównego):
Zasadniczo sztuką jest wywoływanie .get () na każdej przyszłości, zamiast nieskończonego zapętlenia, wywołanie isDone () na (wszystkich lub każdym). Więc masz gwarancję, że „przejdziesz” przez ten blok, jak tylko skończy się ostatni wątek. Zastrzeżenie polega na tym, że ponieważ wywołanie .get () ponownie podnosi wyjątki, jeśli jeden z wątków umrze, możesz podnieść z tego prawdopodobnie przed zakończeniem innych wątków [aby tego uniknąć, możesz dodać
catch ExecutionException
wokół wywołania get ]. Drugim zastrzeżeniem jest to, że zachowuje odniesienie do wszystkich wątków, więc jeśli mają zmienne lokalne wątku, nie zostaną zebrane, dopóki nie przejdziesz przez ten blok (chociaż możesz być w stanie obejść ten problem, jeśli stanie się to problemem, usuwając Future's off the ArrayList). Jeśli chcesz wiedzieć, która przyszłość „kończy jako pierwsza”https://stackoverflow.com/a/31885029/32453źródło
ExecutorCompletionService.take
: stackoverflow.com/a/11872604/199364W Javie 8 możesz to zrobić za pomocą CompletableFuture :
źródło
ExecutorService es = Executors.newFixedThreadPool(4); List< Future<?>> futures = new ArrayList<>(); for(Runnable task : taskList) { futures.add(es.submit(task)); } for(Future<?> future : futures) { try { future.get(); }catch(Exception e){ // do logging and nothing else } }
Tylko moje dwa centy. Aby przezwyciężyć
CountDownLatch
potrzebę wcześniejszej znajomości liczby zadań, możesz to zrobić w tradycyjny sposób, używając prostegoSemaphore
.W swoim zadaniu po prostu zadzwoń
s.release()
jak chceszlatch.countDown();
źródło
release
połączenia miały miejsce przedacquire
połączeniem, ale po przeczytaniu dokumentacji Semafora widzę, że jest w porządku.Trochę późno do gry, ale ze względu na zakończenie ...
Zamiast „czekać” na zakończenie wszystkich zadań, możesz pomyśleć zgodnie z zasadą Hollywood: „nie dzwoń do mnie, zadzwonię” - kiedy skończę. Myślę, że wynikowy kod jest bardziej elegancki ...
Guava oferuje kilka interesujących narzędzi do osiągnięcia tego celu.
Przykład ::
Zawiń ExecutorService w ListeningExecutorService ::
Prześlij zbiór kallabów do wykonania:
Teraz zasadnicza część:
Załącz oddzwonienie do ListenableFuture, którego możesz użyć, aby otrzymywać powiadomienia o zakończeniu wszystkich kontraktów futures:
Daje to również tę zaletę, że po zakończeniu przetwarzania można zebrać wszystkie wyniki w jednym miejscu ...
Więcej informacji tutaj
źródło
runOnUiThread()
wonSuccess()
.CyclicBarrier klasa w Javie 5 i później jest przeznaczony do tego rodzaju rzeczy.
źródło
Wykonaj jedno z poniższych podejść.
submit
naExecutorService
i sprawdzić stan połączenia z blokowaniaget()
naFuture
obiekcie zgodnie z sugestiąKiran
invokeAll()
na ExecutorServiceshutdown, awaitTermination, shutdownNow
interfejsów API ThreadPoolExecutor w odpowiedniej kolejnościPowiązane pytania SE:
Jak CountDownLatch jest używany w wielowątkowości Java?
Jak poprawnie zamknąć java ExecutorService
źródło
oto dwie opcje, trochę mylące, która najlepiej wybrać.
Opcja 1:
Opcja 2:
Tutaj umieszczamy future.get (); w try catch to dobry pomysł, prawda?
źródło
Możesz zawinąć swoje zadania w inny program uruchamialny, który wyśle powiadomienia:
źródło
completed
licznik. Więc po uruchomieniu ich wszystkich, przy każdym powiadomieniu może ustalić, czy wszystkie zadania zostały zakończone. Należy pamiętać, że konieczne jest korzystanie z niegotry/finally
, aby otrzymać gotowe powiadomienie (lub alternatywne powiadomienie wcatch
bloku), nawet jeśli zadanie się nie powiedzie. W przeciwnym razie czekałbym wiecznie.Właśnie napisałem przykładowy program, który rozwiązuje twój problem. Nie podano zwięzłej implementacji, więc dodam jedną. Chociaż możesz używać
executor.shutdown()
iexecutor.awaitTermination()
, nie jest to najlepsza praktyka, ponieważ czas potrzebny na różne wątki byłby nieprzewidywalny.źródło
Wystarczy podać tutaj inne alternatywy niż używać zapadek / barier. Możesz również uzyskać częściowe wyniki, aż wszystkie zakończą się przy użyciu CompletionService .
Z Java Concurrency w praktyce: „Jeśli masz zestaw obliczeń do przesłania do Executora i chcesz odzyskać ich wyniki, gdy staną się dostępne, możesz zachować Przyszłość związaną z każdym zadaniem i wielokrotnie sondować w celu zakończenia, dzwoniąc do get z limit czasu zero. Jest to możliwe, ale uciążliwe . Na szczęście istnieje lepszy sposób : usługa kompletacji ”.
Tutaj wdrożenie
źródło
To jest moje rozwiązanie oparte na wskazówce „AdamSkywalker” i działa
źródło
Możesz użyć tego kodu:
źródło
Utworzyłem następujący działający przykład. Chodzi o to, aby mieć sposób na przetworzenie puli zadań (używam kolejki jako przykładu) z wieloma wątkami (określanymi programowo przez liczbęOfTasks / próg) i poczekaj, aż wszystkie wątki zostaną zakończone, aby kontynuować przetwarzanie.
Mam nadzieję, że to pomoże!
źródło
Możesz użyć własnej podklasy ExecutorCompletionService do zawinięcia
taskExecutor
oraz własnej implementacji BlockingQueue, aby uzyskać informacje o zakończeniu każdego zadania i wykonać dowolne wywołanie zwrotne lub inną akcję, gdy liczba ukończonych zadań osiągnie pożądany cel.źródło
powinieneś użyć
executorService.shutdown()
iexecutorService.awaitTermination
metody.Poniższy przykład:
źródło
Więc zamieszczam tutaj swoją odpowiedź z połączonego pytania, jeśli ktoś chce prostszego sposobu na zrobienie tego
źródło
Java 8 - Możemy używać interfejsu API strumienia do przetwarzania strumienia. Zobacz fragment kodu poniżej
źródło
Jeśli
doSomething()
rzucą inne wyjątki,latch.countDown()
wydaje się, że się nie wykona, więc co powinienem zrobić?źródło
jeśli użyjesz więcej wątku ExecutionServices SEQUENTIALLY i chcesz poczekać na zakończenie KAŻDEJ WYKONAWCZEJ USŁUGI. Najlepszy sposób jest jak poniżej;
źródło
To może pomóc
źródło
Możesz wywołać waitTillDone () w tej klasie Runner :
Możesz ponownie użyć tej klasy i wywołać funkcję waitTillDone () tyle razy, ile chcesz przed wywołaniem shutdown (), a ponadto Twój kod jest wyjątkowo prosty . Również nie trzeba znać się szereg zadań góry.
Aby go użyć, po prostu dodaj tę
compile 'com.github.matejtymes:javafixes:1.3.1'
zależność grad / maven do swojego projektu.Więcej informacji można znaleźć tutaj:
https://github.com/MatejTymes/JavaFixes
źródło
W executorze istnieje metoda,
getActiveCount()
która podaje liczbę aktywnych wątków.Po rozciągnięciu wątku możemy sprawdzić, czy
activeCount()
wartość wynosi0
. Gdy wartość wynosi zero, oznacza to, że nie ma obecnie aktywnych wątków, co oznacza, że zadanie zostało zakończone:źródło