Zwykle tworząc aplikację wielowątkową mamy do czynienia z organizacją pracy wątków. Im większa nasza aplikacja i im więcej wątków potrzebujemy do zorganizowania wykonywania zadań wielowątkowych, tym więcej obiektównadający się do bieganiatworzymy.
Należy tutaj zaznaczyć, że utworzenie wątku w Javie jest dość kosztowną operacją. Jeśli będziemy tworzyć nową instancję wątku za każdym razem, aby wykonać operację, będziemy mieli duże problemy z wydajnością, a co za tym idzie, z kondycją aplikacji.
W tym miejscu z pomocą przychodzą pule wątków i ThreadPoolExecutor .
Pula wątków to zestaw wstępnie zainicjowanych wątków, których rozmiar może być stały lub zmienny.
Jeśli zadań jest więcej niż wątków, zadania czekają w kolejce (kolejka zadań). Z kolejki zadanie zostaje wykonane przez N-ty wątek z puli, a po wykonaniu zadania wątek pobiera nowe zadanie z kolejki. Po wykonaniu wszystkich zadań z kolejki wątki pozostają aktywne i czekają na nowe zadania. Kiedy pojawiają się zadania, wątki również zaczynają je wykonywać.
ThreadPoolExecutor
Począwszy od Javy 5, framework Executor pojawia się w rozwiązaniu wielowątkowym . Ogólnie rzecz biorąc, ma wiele komponentów i przyszedł do nas, aby rozwiązać problem wydajnego zarządzania kolejkami i pulą wątków.
Główne interfejsy to Executor i ExecutorService .
Executor to interfejs z pojedynczą metodą voidexec(Runnable runnable).
Przekazując zadanie do implementacji tej metody, wiedz, że w przyszłości będzie ono wykonywane asynchronicznie.
ExecutorService to interfejs, który dziedziczy z interfejsu Executor i udostępnia narzędzia do wykonywania zadań. Posiada również metody przerywania uruchomionego zadania i kończenia puli wątków.
ThreadPoolExecutor implementuje interfejsy Executor i ExecutorService i oddziela tworzenie zadań od wykonywania zadań. Musimy zaimplementować obiekty Runnable i wysłać je do executora, a ThreadPoolExecutor odpowiada za ich wykonanie, tworzenie instancji i pracę z wątkami.
Po przesłaniu zadania do wykonania wykorzystywany jest utworzony już wątek z puli. Rozwiązuje to problem marnowania zasobów na tworzenie i inicjowanie nowego wątku, a po użyciu - na czyszczenie go przez GC - i poprawia wydajność.
ThreadPoolExecutor ma 4 konstruktorów:
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Parametry ThreadPoolExecutor są przekazywane do konstruktora :
rozmiar puli podstawowej | Parametr, który pokazuje, ile wątków będzie gotowych (uruchomionych) po uruchomieniu usługi executora. |
maksymalny rozmiar puli | Maksymalna liczba wątków, które może utworzyć usługa wykonawcy. |
KeepAliveTime | Czas, w którym uwolniony wątek będzie żył, a następnie zostanie zniszczony, jeśli liczba wątków jest większarozmiar puli podstawowej. Jednostki czasu określane są w kolejnym parametrze. |
jednostka | Jednostki czasu (godziny, minuty, sekundy, milisekundy itd.). |
Kolejka pracy | Implementacja kolejki do zadań. |
treser | Obsługa zadań, których nie można ukończyć. |
Fabryka nici | Obiekt, który tworzy nowe wątki na żądanie. Korzystanie z fabryk wątków usuwa sprzętowe wiązanie wywołań z nowym wątkiem, umożliwiając aplikacjom używanie specjalnych podklas wątków, priorytetów i tak dalej. |
Tworzenie ThreadPoolExecutora
Tworzenie ThreadPoolExecutor może uprościć dla nas narzędzie klasy Executors . Ta klasa narzędziowa posiada metody, które pomogą nam przygotować obiektThreadPoolExecutor.
newFixedThreadPool — tworzy pulę wątków, która ponownie wykorzystuje ustaloną liczbę wątków do uruchamiania dowolnej liczby zadań. |
|
newWorkStealingPool — tworzy pulę wątków, gdzie liczba wątków = liczba rdzeni procesora dostępnych dla JVM. Domyślny poziom równoległości to jeden. Oznacza to, że w puli zostanie utworzonych tyle wątków, ile rdzeni procesora jest dostępnych dla JVM. Jeśli współbieżność wynosi 4, przekazana wartość jest używana zamiast liczby rdzeni. |
|
newSingleThreadExecutor — tworzy pulę z pojedynczym wątkiem do wykonywania wszystkich zadań. |
|
newCachedThreadPool — tworzy pulę wątków, która w razie potrzeby tworzy nowe wątki, ale ponownie wykorzystuje wcześniej utworzone wątki, gdy są dostępne. |
|
newScheduledThreadPool — tworzy pulę wątków, które mogą planować wykonywanie poleceń po określonym opóźnieniu lub wykonywanie okresowe. |
|
W kolejnych wykładach rozważymy każdy z rodzajów basenów.
GO TO FULL VERSION