concurrency java semaphore
W tym samouczku omówimy komponenty pakietu java.util.concurrent, takie jak Java Semaphore, Executor Framework, ExecutorService do implementacji współbieżności w Javie:
Z naszych poprzednich samouczków dotyczących języka Java wiemy, że platforma Java obsługuje programowanie współbieżne od podstaw. Podstawową jednostką współbieżności jest wątek i szczegółowo omówiliśmy wątki i wielowątkowość w Javie.
Począwszy od wersji Java 5, do platformy Java dodano pakiet o nazwie „java.util.concurrent”. Ten pakiet zawiera zestaw klas i bibliotek, które ułatwiają programiście tworzenie aplikacji współbieżnych (wielowątkowych). Korzystając z tego pakietu, nie musimy pisać złożonych klas, ponieważ mamy gotowe implementacje większości współbieżnych koncepcji.
=> Sprawdź WSZYSTKIE samouczki Java tutaj.
W tym samouczku omówimy różne komponenty pakietu java.util.concurrent dotyczące współbieżności i wielowątkowości w Javie.
Czego się nauczysz:
Pakiet java.util.concurrent
Poniżej wymienione są różne komponenty pakietu java.util.concurrent dotyczące współbieżności i wielowątkowości w Javie. Przyjrzyjmy się szczegółowo każdemu komponentowi za pomocą prostych przykładów programowania. Niektóre z komponentów będziemy
omówić są:
- Framework wykonawczy
- ExecutorService
- Pula wątków
- Możliwość wywołania
- Zamki - ReentrantLock
- Semafor
- ForkJoinPool
Framework wykonawczy w Javie
Struktura Executora w Javie została wydana wraz z wydaniem JDK 5. Struktura Executor (java.util.concurrent.Executor) to framework składający się z komponentów, które pomagają nam efektywnie obsługiwać wiele wątków.
Korzystając z Executor Framework, możemy uruchamiać obiekty, które są Runnable poprzez ponowne wykorzystanie już istniejących wątków. Nie musimy tworzyć nowych wątków za każdym razem, gdy musimy uruchomić obiekty.
Executor API oddziela lub odłącza wykonanie zadania od rzeczywistego zadania przy użyciu pliku Wykonawca . Executor jest wyśrodkowany na interfejsie Executora i ma interfejsy podrzędne, tj. ExecutorService i klasą ThreadPoolExecutor.
Zatem używając Executora, musimy po prostu stworzyć obiekty Runnable, a następnie wysłać je do executora, który je wykonuje.
Niektóre z najlepszych praktyk, których należy przestrzegać podczas korzystania z frameworka Executora, to:
- Powinniśmy sprawdzić krzyżowo i zaplanować kod do przeglądu najważniejszych list, abyśmy mogli wykryć zarówno zakleszczenie, jak i livelock w kodzie.
- Kod Java powinien zawsze być wykonywany na narzędziach do analizy statycznej. Przykłady narzędzi do analizy statycznej to FindBugs i PMD.
- Powinniśmy nie tylko wychwytywać wyjątki, ale także błędy w programach wielowątkowych.
Omówmy teraz składniki platformy Executor Framework w Javie.
najlepszy darmowy downloader mp3 dla Androida
Wykonawca
Moduł wykonujący można zdefiniować jako interfejs używany do reprezentowania obiektu wykonującego powierzone mu zadania. To, czy zadanie ma zostać uruchomione w bieżącym, czy nowym wątku, zależy od punktu, w którym zainicjowano wywołanie, co dodatkowo zależy od implementacji.
Więc używając Executora, możemy odłączyć zadania od rzeczywistego zadania, a następnie uruchomić je asynchronicznie.
Jednak wykonanie zadania za pomocą Executora nie musi być asynchroniczne. Wykonawcy mogą również natychmiast wywołać zadanie, używając wątku wywołującego.
Poniżej podano przykładowy fragment kodu do utworzenia instancji Executora:
public class Invoker implements Executor { @Override public void execute (Runnable r_interface) { r_interface.run(); } }
Po utworzeniu wywołującego, jak pokazano powyżej, możemy go użyć do wykonania zadania w następujący sposób.
public void execute () { Executor executor = new Invoker (); executor.execute ( () -> { //perform task }); }
Zwróć uwagę, że jeśli zadanie nie zostanie zaakceptowane przez Executor, zgłosi RejectedExecutionException.
ExecutorService
Usługa ExecutorService (java.util.concurrent.ExecutorService) planuje przesłane zadania zgodnie z dostępnością wątków, a także utrzymuje kolejkę pamięci. ExecutorService działa jako kompletne rozwiązanie do asynchronicznego przetwarzania zadań.
Aby użyć ExecutorService w kodzie, tworzymy klasę Runnable. ExecutorService utrzymuje pulę wątków, a także przypisuje zadania do wątków. Zadania mogą również ustawiać się w kolejce, jeśli wątek jest niedostępny.
Poniżej podano prosty przykład ExecutorService.
import java.util.concurrent.*; public class Main { public static void main(String() args) { //create ExecutorService instance with 10 threads ExecutorService executor_Service = Executors.newFixedThreadPool(10); //assign the service to Runnable instance executor_Service.execute(new Runnable() { @Override public void run() { //print the message System.out.println('Simple Example of ExecutorService!!!'); } }); //shutdown executorService executor_Service.shutdown(); } }
Wynik
W powyższym programie tworzymy prostą instancję ExecutorService z pulą wątków składającą się z 10 wątków. Następnie jest przypisywany do instancji Runnable i wykonywany w celu wydrukowania powyższej wiadomości. Po wydrukowaniu komunikatu ExecutorService jest wyłączany.
Pula wątków
Pula wątków w języku Java to grupa wątków roboczych, których można wielokrotnie używać i przypisywać zadania.
Pula wątków zawiera grupę wątków o stałym rozmiarze. Każdy wątek jest wyciągany z puli wątków i przydzielany do zadania przez dostawcę usług. Po zakończeniu przypisanego zadania wątek jest ponownie przekazywany do puli wątków.
Pula wątków jest korzystna, ponieważ nie musimy tworzyć nowego wątku za każdym razem, gdy zadanie jest dostępne, co zwiększa wydajność. Jest używany w aplikacjach czasu rzeczywistego, które używają serwletu i JSP, w których pule wątków są używane do przetwarzania żądań.
W aplikacjach wielowątkowych pula wątków oszczędza zasoby i pomaga utrzymać równoległość we wstępnie zdefiniowanych limitach.
Poniższy program w języku Java przedstawia pulę wątków w języku Java.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class WorkerThreadClass implements Runnable { private String message; //thread class constructor public WorkerThreadClass(String s){ this.message=s; } //run method for thread public void run() { System.out.println(' Start: '+message); processmessage(); //sleep between start and end System.out.println(' End: '+ message); } //processmessage method => sleeps the thread for 2 sec private void processmessage() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String() args) { //create a ExecutorService instance ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads //create thread instances and execute them for (int i = 0; i <5; i++) { Runnable workerThrd = new WorkerThreadClass('Thread_' + i); executor.execute(workerThrd);//calling execute method of ExecutorService } //shutdown ExecutorService executor.shutdown(); while (!executor.isTerminated()) { } System.out.println('Finished all threads'); } }
Wynik
W powyższych programach istnieje pula wątków składająca się z 5 wątków, które są tworzone przy użyciu metody „newFixedThreadPool”. Następnie wątki są tworzone i dodawane do puli oraz przypisywane do ExecutorService w celu wykonania.
Możliwość wywołania w Javie
Wiemy już, że możemy tworzyć wątki na dwa sposoby. Jedną z metod jest rozszerzenie klasy Thread, a drugą jest implementacja interfejsu Runnable.
Jednak wątki utworzone za pomocą interfejsu Runnable nie mają jednej funkcji, tj. Nie zwracają wyniku, gdy wątek zostanie zakończony lub run () zakończy wykonywanie. Tutaj pojawia się interfejs Callable.
Korzystając z interfejsu Callable definiujemy zadanie tak, aby zwracało wynik. Może również zgłosić wyjątek. Interfejs Callable jest częścią pakietu java.util.concurrent.
Interfejs Callable udostępnia metodę call (), która znajduje się w podobnych wierszach jak metoda run () udostępniana przez interfejs Runnable, z tą jedyną różnicą, że metoda call () zwraca wartość i zgłasza sprawdzony wyjątek.
Metoda call () interfejsu Callable ma następujący prototyp.
public Object call () throws Exception;
Ponieważ metoda call () zwraca Object, główny wątek musi być tego świadomy.
Stąd wartość zwracana powinna być przechowywana w innym obiekcie znanym w głównym wątku. Służy temu obiekt „Future”. Obiekt Future to obiekt, który przechowuje wynik zwrócony przez wątek. Innymi słowy, zachowa wynik, gdy zwróci Callable.
Calllable hermetyzuje zadanie, które powinno zostać uruchomione w innym wątku. Obiekt Future przechowuje wynik zwrócony z innego wątku.
Do tworzenia wątku nie można użyć interfejsu wywoływalnego. Potrzebujemy Runnable, aby utworzyć wątek. Następnie do przechowywania wyniku wymagany jest obiekt Future. Java udostępnia konkretny typ o nazwie „FutureTask”, który łączy w sobie funkcjonalność, implementując zarówno Runnable, jak i Future.
Tworzymy FutureTask, dostarczając konstruktorowi Callable. Ten obiekt FutureTask jest następnie przekazywany konstruktorowi klasy Thread w celu utworzenia obiektu Thread.
Poniżej podano program w języku Java, który demonstruje interfejs Callable i obiekt Future. W tym programie używamy również obiektu FutureTask.
Jak już wspomniano, w programie tworzymy klasę implementującą interfejs Callable z nadpisaną metodą call (). W metodzie głównej tworzymy 10 obiektów FutureTask. Każdy konstruktor obiektów ma jako argument obiekt klasy Callable. Następnie obiekt FutureTask jest powiązany z instancją wątku.
Stąd pośrednio tworzymy wątek za pomocą obiektu interfejsu Callable.
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //create a class implementing Callable interface class CallableDemo implements Callable { //define call () method public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(10); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class Main { public static void main(String() args) throws Exception { // Array of FutureTask objects FutureTask() randomNumberTasks = new FutureTask(10); for (int i = 0; i <10; i++) { Callable callable = new CallableDemo(); // Create the FutureTask with Callable class randomNumberTasks(i) = new FutureTask(callable); // create thread with FutureTask Thread t = new Thread(randomNumberTasks(i)); //start the thread t.start(); } System.out.println('The contents of FutureTask objects:'); for (int i = 0; i < 10; i++) { // get() contents of FutureTask System.out.print(randomNumberTasks(i).get() + ' '); } } }
Wynik
Jak pokazano w powyższym programie, metoda call () funkcji Callable, która jest nadpisywana w klasie implementującej Callable, generuje liczby losowe. Po uruchomieniu wątku wyświetla te losowe liczby.
W funkcji main używamy również obiektów FutureTask. Ponieważ implementuje interfejs Future, nie musimy przechowywać wyników w obiektach Thread. Podobnie możemy anulować zadanie, sprawdzić, czy jest uruchomione, czy ukończone, a także uzyskać wynik za pomocą obiektu FutureTask.
ReentrantLock w Javie
Omówiliśmy szczegółowo synchronizację wątków przy użyciu słowa kluczowego synchronized w naszym ostatnim samouczku. Użycie słowa synchronizowanego do synchronizacji wątków jest metodą podstawową i jest dość sztywne.
Za pomocą słowa kluczowego synchronized wątek może zostać zablokowany tylko raz. Ponadto, gdy jeden wątek opuści zsynchronizowany blok, następny wątek przejmuje blokadę. Nie ma kolejki oczekujących. Te problemy mogą spowodować zablokowanie innego wątku, ponieważ może on nie uzyskać dostępu do zasobów przez długi czas.
Aby rozwiązać te problemy, potrzebujemy elastycznej metody synchronizacji wątków. „Reentrant Locks” to ta metoda w Javie, która zapewnia synchronizację ze znacznie większą elastycznością.
Klasa „ReentrantLock” implementuje blokady Reentrant i jest częścią pakietu „import java.util.concurrent.locks”. Klasa ReentrantLock zapewnia synchronizację metod w celu uzyskania dostępu do współdzielonych zasobów. Klasy mają również metody blokowania i odblokowywania do blokowania / odblokowywania zasobów w przypadku dostępu do nich przez wątki.
Jedną z charakterystycznych cech ReentrantLock jest to, że wątek może blokować współdzielony zasób więcej niż raz za pomocą ReentrantLock. Zapewnia liczbę wstrzymań, która jest ustawiona na jeden, gdy wątek blokuje zasób.
Wątek może ponownie wejść do zasobu i uzyskać do niego dostęp przed odblokowaniem. Za każdym razem, gdy wątek uzyskuje dostęp do zasobu za pomocą blokady Reentrant, liczba wstrzymań jest zwiększana o jeden. Przy każdym odblokowaniu liczba wstrzymań jest zmniejszana o jeden.
Gdy liczba wstrzymań osiągnie 0, współdzielony zasób zostanie odblokowany.
Klasa ReentrantLock udostępnia również parametr fairness, który jest wartością logiczną, którą można przekazać za pomocą konstruktora blokady. Gdy parametr fairness jest ustawiony na true, to za każdym razem, gdy jeden wątek zwalnia blokadę, blokada jest przekazywana do najdłużej oczekującego wątku. Zapobiega to głodowi.
Blokady wpuszczane mogą być używane w następujący sposób:
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } }
Należy pamiętać, że instrukcja odblokowania dla ReentrantLock jest zawsze w ostatnim bloku. Gwarantuje to, że blokada zostanie zwolniona, nawet jeśli zostanie zgłoszony wyjątek.
Zaimplementujmy program w języku Java, aby zrozumieć funkcję ReentrantLock.
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; //thread class that implements Runnable interface class ThreadClass implements Runnable { String task_name; //define ReentrantLock object ReentrantLock thrd_lck; //ThreadClass constructor initialized lock and task name public ThreadClass(ReentrantLock r_lock, String t_name) { thrd_lck = r_lock; task_name = t_name; } //thread run () method public void run() { boolean bool_val = false; while (!bool_val) { //check for Outer Lock boolean tryLock_val = thrd_lck.tryLock(); // if lock is free, do the following if(tryLock_val) { try { for(int i=0;i<=6;i++) { if(i>=2) { thrd_lck.lock(); Thread thread_one = new Thread(); System.out.println('Thread Created.....'); if(i==3) { thread_one.setName('Maint Thread2'); System.out.println('Thread Created.....'); } } if(i==4) thrd_lck.unlock(); break; } System.out.println('ReentrantLock=>Is locked after sleep(1500) : ' + thrd_lck.isLocked()); System.out.println('Work done for task : ' + task_name ); bool_val = true; } catch(Exception e) { e.printStackTrace(); } } } } } public class Main { public static void main(String() args) { //define ReentrantLock lock object and service pool ReentrantLock reentrant_lock = new ReentrantLock(); ExecutorService pool = Executors.newFixedThreadPool(2); //create thread instance and pass lock and task name Runnable worker_thread = new ThreadClass(reentrant_lock, 'ThreadJob'); //execute the thread in exec pool pool.execute(worker_thread); //shut down the pool pool.shutdown(); } }
Wynik
W powyższym programie stworzyliśmy wątek i użyliśmy do niego ReentrantLock. Za pomocą ReentrantLock można uzyskać dostęp do współdzielonego zasobu.
Semafor w Javie
Następną metodą synchronizacji wątków jest użycie Semaphore. Korzystając z tej konstrukcji zwanej semaforem, dostęp do współdzielonego zasobu jest kontrolowany przez licznik. Sygnały są przesyłane między wątkami, dzięki czemu możemy strzec krytycznej sekcji, a także uniknąć utraty sygnałów.
Semafor można zdefiniować jako zmienną używaną do zarządzania współbieżnymi procesami poprzez synchronizację tych procesów. Semafory są również używane do synchronizowania dostępu do udostępnionego zasobu, a tym samym do uniknięcia sytuacji wyścigu. Zezwolenie udzielone wątkowi na dostęp do udostępnionego zasobu przez semafor jest również nazywane zezwoleniem.
darmowy optymalizator systemu dla Windows 7
W zależności od pełnionych funkcji semafory można podzielić na dwa typy:
# 1) Semafor binarny: Semafor binarny służy do synchronizacji współbieżnych procesów i implementacji wzajemnego wykluczania. Semafor binarny przyjmuje tylko dwie wartości, tj. 0 i 1.
# 2) Liczenie semaforów: Semafor liczący ma wartość wskazującą liczbę procesów, które mogą wejść do sekcji krytycznej. W dowolnym momencie wartość wskazuje maksymalną liczbę procesów, które wchodzą do sekcji krytycznej.
Jak więc działa semafor?
Działanie semafora można podsumować w następujących krokach:
- Jeśli liczba semaforów> 0, oznacza to, że wątek ma zezwolenie na dostęp do sekcji krytycznej, a następnie liczba jest zmniejszana.
- W przeciwnym razie wątek zostanie zablokowany do czasu uzyskania zezwolenia.
- Gdy wątek kończy uzyskiwanie dostępu do udostępnionego zasobu, zezwolenie jest zwalniane, a liczba semaforów jest zwiększana, tak aby inny wątek mógł powtórzyć powyższe kroki i uzyskać zezwolenie.
Powyższe etapy działania semaforów można podsumować na poniższym schemacie blokowym.
W Javie nie musimy implementować naszego semafora, ale zapewnia on rozszerzenie Semafor klasa implementująca funkcjonalność semafora. Klasa Semaphore jest częścią java.util.concurrent pakiet.
Klasa Semaphore udostępnia następujące konstruktory, za pomocą których możemy stworzyć obiekt semaphore:
Semaphore (int num_value) Semaphore (int num_value, boolean how)
Tutaj,
num_value => początkowa wartość liczby zezwoleń, która określa liczbę wątków, które mogą uzyskać dostęp do współdzielonego zasobu.
jak => ustawia kolejność, w jakiej wątkom zostaną udzielone zezwolenia (jak = prawda). Jeśli how = false, to nie jest przestrzegana taka kolejność.
Teraz zaimplementujemy program w języku Java, który zademonstruje semafor używany do zarządzania dostępem do współdzielonych zasobów i zapobiegania sytuacjom wyścigu.
import java.util.concurrent.*; //class for shared resource class SharedRes { static int count = 0; } class ThreadClass extends Thread { Semaphore sem; String threadName; public ThreadClass(Semaphore sem, String threadName) { super(threadName); this.sem = sem; this.threadName = threadName; } @Override public void run() { // Thread T1 processing if(this.getName().equals('T1')) { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ' :waiting for a permit.'); // acquire the permit sem.acquire(); System.out.println(threadName + ':Acquired permit'); // access shared resource for(int i=0; i <5; i++) { SharedRes.count++; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit'); sem.release(); } // Thread T2 processing else { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ':waiting for a permit.'); // acquire the lock sem.acquire(); System.out.println(threadName + ':Acquired permit'); // process the shared resource for(int i=0; i < 5; i++) { SharedRes.count--; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit.'); sem.release(); } } } public class Main { public static void main(String args()) throws InterruptedException { //create Semaphore=> #permits = 1 Semaphore sem = new Semaphore(1); // Create thread instances T1 & T2 //T1=> Increments the count; T2=> Decrements the count ThreadClass thread1 = new ThreadClass(sem, 'T1'); ThreadClass thread2 = new ThreadClass(sem, 'T2'); // start T1 & T2 thread1.start(); thread2.start(); // Wait T1 & T2 thread1.join(); thread2.join(); System.out.println('count: ' + SharedRes.count); // display final count. } }
Wynik
Ten program zadeklarował klasę dla współdzielonego zasobu. Deklaruje również klasę wątku, w której mamy zmienną semaforową, która jest inicjowana w konstruktorze klasy.
W przesłoniętej metodzie run () klasy Thread odbywa się przetwarzanie wystąpienia wątku, w którym wątek uzyskuje zezwolenie, uzyskuje dostęp do udostępnionego zasobu, a następnie zwalnia zezwolenie.
W głównej metodzie zadeklarowaliśmy dwa wystąpienia wątku. Oba wątki są następnie uruchamiane, a następnie czekają przy użyciu metody łączenia. Na koniec wyświetlana jest liczba, tj. 0, co oznacza, że oba wątki zakończyły korzystanie z udostępnionego zasobu.
Rozwidlaj i dołącz w Javie
Struktura rozwidlenia / złączenia została po raz pierwszy wprowadzona w Javie 7. Struktura ta zawiera narzędzia, które mogą przyspieszyć przetwarzanie równoległe. Wykorzystuje wszystkie dostępne rdzenie procesorów w systemie i wykonuje zadanie. Struktura rozwidlenia / złączenia wykorzystuje podejście dziel i zwyciężaj.
Podstawową ideą frameworka Fork / Join jest to, że pierwszy framework „Forks”, tj. Rekurencyjnie dzieli zadanie na mniejsze indywidualne podzadania, aż zadania będą atomowe, tak aby mogły być wykonywane asynchronicznie.
Po wykonaniu tej czynności zadania są „łączone”, tj. Wszystkie podzadania są łączone rekurencyjnie w jedno zadanie lub zwracają wartość.
Struktura rozwidlenia / złączenia zawiera pulę wątków znaną jako „ForkJoinPool”. Pula ta zarządza wątkami roboczymi typu „ForkJoinWorkerThread”, zapewniając w ten sposób efektywne przetwarzanie równoległe.
ForkJoinPool zarządza wątkami roboczymi, a także pomaga nam uzyskać informacje dotyczące wydajności i stanu puli wątków. ForkJoinPool jest implementacją „ExecutorService”, o którym mówiliśmy powyżej.
W przeciwieństwie do wątków roboczych, ForkJoinPool nie tworzy osobnego wątku dla każdego podzadania. Każdy wątek w ForkJoinPool zachowuje swój deque (kolejka z podwójnym zakończeniem) do przechowywania zadań.
Deque działa jako równoważenie obciążenia wątku i robi to za pomocą „algorytmu kradzieży pracy”, który opisano poniżej.
Algorytm kradzieży pracy
Możemy zdefiniować algorytm kradzieży pracy w prostych słowach, takich jak „Jeśli wątek jest wolny,„ ukradnij ”pracę zajętym wątkom”.
Wątek roboczy zawsze będzie pobierał zadania ze swojego deque. Kiedy wszystkie zadania w deque są wyczerpane, a deque jest pusty, wątek roboczy pobierze zadanie z ogona innego deque lub z „globalnej kolejki wpisów”.
W ten sposób możliwość konkurowania wątków o zadania jest zminimalizowana, a także zmniejsza się liczba przypadków, w których wątek musi szukać pracy. Dzieje się tak, ponieważ wątek ma już największą dostępną część pracy i zakończył ją.
Jak więc możemy użyć ForkJoinPool w programie?
Ogólna definicja ForkJoinPool jest następująca:
public class ForkJoinPool extends AbstractExecutorService
Klasa ForkJoinPool jest częścią pakietu „java.util.concurrent”.
W Javie 8 tworzymy instancję ForkJoinPool za pomocą jej statycznej metody „common-pool ()”, która dostarcza odniesienie do wspólnej puli lub domyślnej puli wątków.
ForkJoinPool commonPool = ForkJoinPool.commonPool ();
W Javie 7 tworzymy instancję ForkJoinPool i przypisujemy ją do pola klasy użytkowej, jak pokazano poniżej.
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
Powyższa definicja wskazuje, że pula ma poziom równoległości równy 2, tak że będzie używać 2 rdzeni procesorów.
Aby uzyskać dostęp do powyższej puli, możemy podać następujące oświadczenie.
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
Podstawowym typem zadań ForkJoinPool jest „ForkJoinTask”. Powinniśmy rozszerzyć jedną z jego podklas, tj. Dla zadań void, RecursiveAction i dla zadań zwracających wartość, RecursiveTask. Obie klasy rozszerzone udostępniają abstrakcyjną metodę compute (), w której definiujemy logikę zadania.
Poniżej podano przykład demonstrujący działanie ForkJoinPool.
lista aplikacji szpiegowskich na Androida
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; //class declaration for ForkJoinPool tasks class FJPoolTask extends RecursiveAction { private long Load = 0; public FJPoolTask(long Load) { this.Load = Load; } @Override protected void compute() { //if threshold is reached, break tasks into smaller tasks List subtasks = new ArrayList(); subtasks.addAll(createSubtasks()); for(RecursiveAction subtask : subtasks){ subtask.fork(); } } //create subtasks private List createSubtasks() { List sub_tasks =new ArrayList(); FJPoolTask sub_task1 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task2 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task3 = new FJPoolTask(this.Load / 2); sub_tasks.add(sub_task1); sub_tasks.add(sub_task2); sub_tasks.add(sub_task3); return sub_tasks; } } public class Main { public static void main(final String() arguments) throws InterruptedException { //get count of available processors int proc = Runtime.getRuntime().availableProcessors(); System.out.println('Processors available:' +proc); //declare forkJoinPool ForkJoinPool Pool = ForkJoinPool.commonPool(); System.out.println(' Active Threads (Before invoke):' +Pool.getActiveThreadCount()); //Declare ForkJoinPool task object FJPoolTask t = new FJPoolTask(400); //submit the tasks to the pool Pool.invoke(t); System.out.println(' Active Threads (after invoke):' +Pool.getActiveThreadCount()); System.out.println('Common Pool Size :' +Pool.getPoolSize()); } }
Wynik
W powyższym programie znajdujemy liczbę aktywnych wątków w systemie przed i po wywołaniu metody „invoke ()”. Metoda invoke () służy do przesyłania zadań do puli. Znajdziemy również liczbę dostępnych rdzeni procesorów w systemie.
Często Zadawane Pytania
P # 1) Co to jest Java Util Concurrent?
Odpowiedź: Pakiet „java.util.concurrent” to zestaw klas i interfejsów udostępnianych przez Javę w celu ułatwienia tworzenia aplikacji współbieżnych (wielowątkowych). Korzystając z tego pakietu możemy bezpośrednio korzystać z interfejsu i klas, a także API bez konieczności pisania naszych klas.
Pytanie 2) Które z poniższych są współbieżnymi implementacjami obecnymi w java.util. pakiet równoległy?
Odpowiedź: Na wysokim poziomie pakiet java.util.concurrent zawiera narzędzia takie jak moduły wykonawcze, synchronizatory, kolejki, czasy i kolekcje współbieżne.
Pytanie 3) Co to jest przyszła Java?
Odpowiedź: Obiekt Future (java.util.concurrent.Future) służy do przechowywania wyniku zwróconego przez wątek po zaimplementowaniu interfejsu Callable.
Pytanie 4) Co jest bezpieczne wątkowo w Javie?
Odpowiedź: Kod lub klasa bezpieczna dla wątków w Javie to kod lub klasa, które można bez problemu udostępniać w środowisku wielowątkowym lub współbieżnym i daje oczekiwane rezultaty.
Pytanie 5) Co to jest zsynchronizowana kolekcja w Javie?
Odpowiedź: Zsynchronizowana kolekcja to kolekcja bezpieczna wątkowo. Metoda synchronized collection () klasy java.util.Collections zwraca kolekcję zsynchronizowaną (bezpieczną wątkowo).
Wniosek
W tym samouczku zakończyliśmy temat wielowątkowości i współbieżności w Javie. Omówiliśmy szczegółowo wielowątkowość w naszych poprzednich samouczkach. Tutaj omówiliśmy współbieżność i implementację związaną ze współbieżnością i wielowątkowością, które są częścią pakietu java.util.concurrent.
Omówiliśmy jeszcze dwie metody synchronizacji, semafory i ReentrantLock. Omówiliśmy również ForkJoinPool, który służy do wykonywania zadań, dzieląc je na prostsze zadania, a następnie ostatecznie łącząc wynik.
Pakiet java.util.concurrent obsługuje również strukturę Executora i moduły wykonawcze, które pomagają nam wykonywać wątki. Omówiliśmy również implementację puli wątków, która składa się z wątków wielokrotnego użytku, które są zwracane do puli po zakończeniu wykonywania.
Omówiliśmy inny interfejs podobny do Runnable, który również pomaga nam zwrócić wynik z wątku i obiektu Future używanego do przechowywania uzyskanego wyniku wątku.
=> Obejrzyj serię prostych szkoleń dotyczących języka Java.
rekomendowane lektury
- Thread.Sleep () - metoda Thread Sleep () w języku Java z przykładami
- Wdrażanie Java: tworzenie i wykonywanie pliku Java JAR
- Podstawy języka Java: składnia języka Java, klasa języka Java i podstawowe pojęcia dotyczące języka Java
- Wirtualna maszyna Java: jak JVM pomaga w uruchamianiu aplikacji Java
- Modyfikatory dostępu w Javie - samouczek z przykładami
- Synchronizacja Java: Co to jest synchronizacja wątków w języku Java
- Samouczek JAVA dla początkujących: ponad 100 praktycznych samouczków wideo Java
- Klasa Java Integer i Java BigInteger z przykładami