Wydaje się, że niemożliwe jest utworzenie buforowanej puli wątków z ograniczeniem liczby wątków, które może ona utworzyć.
Oto jak static Executors.newCachedThreadPool jest zaimplementowane w standardowej bibliotece Java:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Tak więc, używając tego szablonu do utworzenia puli wątków w pamięci podręcznej o stałym rozmiarze:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());
Teraz, jeśli użyjesz tego i prześlesz 3 zadania, wszystko będzie dobrze. Przesłanie dalszych zadań spowoduje odrzucenie wyjątków wykonania.
Próbuję tego:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());
Spowoduje, że wszystkie wątki będą wykonywane sekwencyjnie. Oznacza to, że pula wątków nigdy nie utworzy więcej niż jednego wątku do obsługi zadań.
To jest błąd w metodzie wykonywania programu ThreadPoolExecutor? A może jest to zamierzone? Czy jest inny sposób?
Edycja: chcę coś dokładnie takiego jak buforowana pula wątków (tworzy wątki na żądanie, a następnie je zabija po pewnym czasie), ale z ograniczeniem liczby wątków, które może utworzyć i możliwością kontynuowania kolejkowania dodatkowych zadań, gdy ma osiągnął limit wątków. Zgodnie z odpowiedzią sjlee jest to niemożliwe. Patrząc na metodę execute () ThreadPoolExecutor, jest to rzeczywiście niemożliwe. Musiałbym podklasować ThreadPoolExecutor i przesłonić execute (), podobnie jak robi to SwingWorker, ale to, co robi SwingWorker w swoim execute (), to kompletny hack.
źródło
Odpowiedzi:
ThreadPoolExecutor ma następujące kilka kluczowych zachowań, a Twoje problemy można wyjaśnić za pomocą tych zachowań.
Po przesłaniu zadań
W pierwszym przykładzie zwróć uwagę, że SynchronousQueue ma zasadniczo rozmiar 0. W związku z tym w momencie osiągnięcia maksymalnego rozmiaru (3) zaczyna obowiązywać zasada odrzucania (# 4).
W drugim przykładzie wybraną kolejką jest LinkedBlockingQueue o nieograniczonym rozmiarze. Dlatego utkniesz z zachowaniem # 2.
Nie możesz naprawdę majstrować przy typie buforowanym lub typie stałym, ponieważ ich zachowanie jest prawie całkowicie określone.
Jeśli chcesz mieć ograniczoną i dynamiczną pulę wątków, musisz użyć dodatniego rozmiaru rdzenia i maksymalnego rozmiaru w połączeniu z kolejką o skończonym rozmiarze. Na przykład,
Dodatek : jest to dość stara odpowiedź i wydaje się, że JDK zmienił swoje zachowanie, jeśli chodzi o rozmiar rdzenia równy 0. Od JDK 1.6, jeśli rozmiar rdzenia wynosi 0, a pula nie zawiera żadnych wątków, ThreadPoolExecutor doda wątku, aby wykonać to zadanie. Dlatego rozmiar rdzenia równy 0 jest wyjątkiem od powyższej reguły. Dziękuję Steve za zwrócenie mi na to uwagi.
źródło
allowCoreThreadTimeOut
aby ta odpowiedź była doskonała. Zobacz odpowiedź @ user1046052O ile czegoś nie przeoczyłem, rozwiązanie pierwotnego pytania jest proste. Poniższy kod implementuje żądane zachowanie zgodnie z opisem w oryginalnym plakacie. Pojawi się do 5 wątków, aby pracować w nieograniczonej kolejce, a bezczynne wątki zostaną zakończone po 60 sekundach.
źródło
Miał ten sam problem. Ponieważ żadna inna odpowiedź nie łączy wszystkich problemów, dodaję moje:
Jest teraz wyraźnie napisane dokumentacji : jeśli używasz kolejki, która nie blokuje (
LinkedBlockingQueue
) maksymalnej liczby wątków, ustawienie nie ma wpływu, używane są tylko podstawowe wątki.więc:
Ten wykonawca ma:
Brak koncepcji maksymalnej liczby wątków, ponieważ używamy nieograniczonej kolejki. Jest to dobra rzecz, ponieważ taka kolejka może spowodować, że moduł wykonawczy utworzy ogromną liczbę dodatkowych wątków niebędących rdzeniami, jeśli będzie postępować zgodnie ze swoją zwykłą polityką.
Kolejka o maksymalnym rozmiarze
Integer.MAX_VALUE
.Submit()
wyrzuci,RejectedExecutionException
jeśli liczba oczekujących zadań przekroczyInteger.MAX_VALUE
. Nie jestem pewien, czy najpierw zabraknie nam pamięci, bo tak się stanie.Możliwe 4 wątki rdzeniowe. Wątki bezczynności są automatycznie zamykane, jeśli są bezczynne przez 5 sekund.Tak więc, tak, wątki ściśle na żądanie.Liczba może być zmieniana za pomocą
setThreads()
metody.Zapewnia, że minimalna liczba głównych wątków nigdy nie jest mniejsza niż jeden, w przeciwnym razie
submit()
odrzuci każde zadanie. Ponieważ wątki podstawowe muszą być> = max wątkami, metodasetThreads()
ustawia również maksymalną liczbę wątków, chociaż ustawienie maksymalnego wątku jest bezużyteczne dla nieograniczonej kolejki.źródło
W pierwszym przykładzie kolejne zadania są odrzucane, ponieważ
AbortPolicy
jest to ustawienie domyślneRejectedExecutionHandler
. ThreadPoolExecutor zawiera następujące zasady, które można zmienić za pomocąsetRejectedExecutionHandler
metody:Wygląda na to, że chcesz buforować pulę wątków z CallerRunsPolicy.
źródło
Żadna z odpowiedzi tutaj nie rozwiązała mojego problemu, który dotyczył tworzenia ograniczonej liczby połączeń HTTP przy użyciu klienta HTTP Apache (wersja 3.x). Ponieważ wymyślenie dobrej konfiguracji zajęło mi kilka godzin, podzielę się:
Tworzy to,
ThreadPoolExecutor
który zaczyna się od pięciu i przechowuje maksymalnie dziesięć jednocześnie uruchomionych wątków używanychCallerRunsPolicy
do wykonywania.źródło
Zgodnie z Javadoc dla ThreadPoolExecutor:
(Podkreśl moje.)
Odpowiedź jittera jest tym, czego chcesz, chociaż moja odpowiada na twoje inne pytanie. :)
źródło
jest jeszcze jedna opcja. Zamiast używać nowej kolejki SynchronousQueue, możesz również użyć dowolnej innej kolejki, ale musisz upewnić się, że jej rozmiar to 1, aby zmusić usługę executorservice do utworzenia nowego wątku.
źródło
Nie wygląda na to, żeby którakolwiek z odpowiedzi faktycznie odpowiadała na pytanie - w rzeczywistości nie widzę sposobu na zrobienie tego - nawet jeśli podklasujesz z PooledExecutorService, ponieważ wiele metod / właściwości jest prywatnych, np. Tworzenie addIfUnderMaximumPoolSize było chronione, możesz wykonaj następujące czynności:
Najbliżej mi było to - ale nawet to nie jest zbyt dobrym rozwiązaniem
ps nie testował powyższego
źródło
Oto inne rozwiązanie. Myślę, że to rozwiązanie zachowuje się tak, jak chcesz (choć nie jest z niego dumne):
źródło
To jest to, czego chcesz (przynajmniej tak mi się wydaje). Aby uzyskać wyjaśnienie, sprawdź odpowiedź Jonathana Feinberga
Executors.newFixedThreadPool(int n)
źródło
Możesz użyć
ThreadPoolExecutor
zgodnie z sugestią @sjleeMożesz dynamicznie kontrolować wielkość puli. Spójrz na to pytanie, aby uzyskać więcej informacji:
Dynamiczna pula wątków
LUB
Możesz użyć interfejsu API newWorkStealingPool , który został wprowadzony w java 8.
Domyślnie poziom równoległości jest ustawiony na liczbę rdzeni procesora na serwerze. Jeśli masz serwer z 4 rdzeniami procesora, rozmiar puli wątków wynosiłby 4. Ten interfejs API zwraca
ForkJoinPool
typExecutorService
i umożliwia kradzież pracy bezczynnych wątków poprzez kradzież zadań z zajętych wątków w ForkJoinPool.źródło
Problem został podsumowany następująco:
Zanim wskażę rozwiązanie, wyjaśnię, dlaczego poniższe rozwiązania nie działają:
Nie spowoduje to kolejkowania zadań po osiągnięciu limitu 3, ponieważ SynchronousQueue z definicji nie może przechowywać żadnych elementów.
Nie spowoduje to utworzenia więcej niż jednego wątku, ponieważ ThreadPoolExecutor tworzy tylko wątki przekraczające corePoolSize, jeśli kolejka jest pełna. Ale LinkedBlockingQueue nigdy nie jest pełny.
Nie spowoduje to ponownego użycia wątków, dopóki nie zostanie osiągnięty corePoolSize, ponieważ ThreadPoolExecutor zwiększa liczbę wątków do momentu osiągnięcia corePoolSize, nawet jeśli istniejące wątki są bezczynne. Jeśli możesz żyć z tą wadą, to jest to najłatwiejsze rozwiązanie problemu. Jest to również rozwiązanie opisane w artykule „Współbieżność języka Java w praktyce” (przypis na str. 172).
Wydaje się, że jedynym kompletnym rozwiązaniem opisanego problemu jest przesłonięcie metody kolejki
offer
i napisanie a,RejectedExecutionHandler
jak wyjaśniono w odpowiedziach na to pytanie: Jak sprawić, aby ThreadPoolExecutor zwiększył wątki do maksimum przed kolejkowaniem?źródło
Działa to dla Java8 + (i innych, na razie ..)
gdzie 3 to limit liczby wątków, a 5 to limit czasu dla bezczynnych wątków.
Jeśli chcesz sprawdzić, czy to działa samodzielnie , oto kod do wykonania pracy:
wyjście powyższego kodu dla mnie to
źródło