niedziela, 22 czerwca 2014

Wielowątkowość - Executor framework

Wykonywanie zadań bezpośrednio

 

Bezpośrednio tworzenie i wykonywanie zadań ma szereg wad:
  • tworzenie i kończenie pracy wątku nie jest darmową operacją i opóźnia wykonanie wątku, jeśli wątków jest wiele a zadanie do wykonania proste to uwidacznia się to jeszcze bardziej zwiększając drastycznie opóźnienie (latency)
  • każdy wątek ma swój stos - konsumuje pamięć którą jest przydzielona JVM
  • nieograniczenie tworzenie wątków może spowodować że systemowi zabraknie zasobów i przestanie działać
 

Executor serivce

 

Executor framework pozwala na oddzielenie tworzenia zadań i od ich uruchamiania. Do uruchamiania zadań służy np klasa ThreadPoolExecutor.

Aby stworzyć executora do wykonywania zadań można skorzystać z klasy Excutors i metody fabrykującej newFixedThreadPool dla executora ze stałą liczbą wątków lub newCachedThreadPool dla executora bez ograniczenia liczby wątków.

Executor nie tworzy dla każdego zadania nowego wątku - jeśli wątek zakończy pracę, bierze następne zadanie a do kolejkowania zadań służy kolekcja blokująca (BlockingQueue).

Używanie interfejsu ExecutorService pozwala nam w każdym momencie podmienić sposób wykonywania zadań, i ustalić wiele innych właściwości:
  • w którym wątku zadanie będzie wykonywane
  • w jakiej kolejności zadania będą wykonywane
  • ile zadań może działać naraz
  • jak wiele zadań może być kolejkowanych
  • specyfikacje czy task może być odrzucony z powodu przeciążenia i w jaki sposób ma to być zrobione
  • czy przed i po wykonaniu zadania wykonać inne zadanie
Wszystko to można dokonać przez użycie bezpośrednio konstruktorów np klasy ThreadPoolExecutor.

ExecutorService ma trzy stanu - running, shutting down, terminated. Metoda shutdown zaczyna bezpieczne zamykanie - wszystkie submitowane i działające taski są kończone a następnie executor jest zamykany. Możliwe jest też użycie metody shotdownNow które stara się anulować aktualnie działające zadania i nie startuje nowych.
Należy pamiętać że aby maszyna wirtualna się zamknęła nie mogą działać żadne jej wątki które nie są demonami więc należy zamknąć ExecutorService aby skończyć jego wątki. Najlepiej zrobić to wykonując metodę shutdown a następnie awaitTermination z odpowiednio dużym czasem.

Inne rodzaje ExecutorService:
  • ForkJoinPool - do wykonywania specjalnych zadań ForkJoinTask  które umożliwiają łatwą implementacje problemów które można podzielić na mniejsze
  • ScheduledThreadPoolExecutor - do tworzenia zadań które będą dostępne (wykonywane) w określonym czasie
 

CompletionService


Pakiet java.util.concurent zawiera także interfejs CompletionService i jego klasę implementującą ExecutorCompletionService. Dzieki tej klasie można w łatwy sposób pobrać wyniki wykonanych zadań jeśli są one potrzebne - nie trzeba pobierać Future dla każdego zadania i iterować po tym. Dzięki temu dostaniemy pierwszy wynik zaraz po jego zakończeniu bo wyniki zapisywane są na kolejce blokującej.
ExecutionCompletionService potrzebuje executora aby działać - więc można tworzyć wiele takich serwisów dla jednego executora, można także przekazać implementację kolejki blokującej której chcemy użyć dla skończonych zadań.

Brak komentarzy:

Prześlij komentarz