concurrency java semaphore
Tento tutoriál bude diskutovať o komponentoch balíka java.util.concurrent ako Java Semaphore, Executor Framework, ExecutorService na implementáciu Concurrency v Jave:
Z našich predchádzajúcich výukových programov pre Java vieme, že platforma Java podporuje súbežné programovanie od základov. Základnou jednotkou súbežnosti je vlákno a podrobne sme si rozobrali vlákna a viacvláknové spracovanie v Jave.
Od Javy 5 a ďalej bol na platformu Java pridaný balík s názvom „java.util.concurrent“. Tento balík obsahuje množinu tried a knižníc, ktoré uľahčujú programátorovi vývoj súbežných (viacvláknových) aplikácií. Pomocou tohto balíka nemusíme písať zložité triedy, pretože máme pripravené implementácie väčšiny súbežných konceptov.
=> Skontrolujte VŠETKY návody Java tu.
V tomto tutoriáli sa budeme venovať rôznym komponentom balíka java.util.concurrent týkajúcich sa súbežnosti a multithreadingu v Jave.
Čo sa dozviete:
Balík java.util.concurrent
Nižšie sú uvedené rôzne komponenty balíka java.util.concurrent týkajúce sa súbežnosti a multithreadingu v Jave. Pozrime sa na jednotlivé komponenty podrobne pomocou jednoduchých príkladov programovania. Niektoré komponenty budeme
diskutovať sú:
- Rámec exekútora
- ExecutorService
- ThreadPool
- Vyvolávateľná
- Zámky - ReentrantLock
- Semafor
- ForkJoinPool
Exekútorský rámec v Jave
Executor Framework v Jave bol vydaný s vydaním JDK 5. Exekútorský rámec (java.util.concurrent.Executor) je rámec, ktorý sa skladá z komponentov, ktoré nám pomáhajú efektívne zvládnuť viac vlákien.
Pomocou rámca Executor Framework môžeme spúšťať objekty, ktoré sú spustiteľné, a to opätovným použitím už existujúcich vlákien. Nemusíme vytvárať nové vlákna vždy, keď potrebujeme spúšťať objekty.
Rozhranie Executor API oddeľuje alebo rozkladá vykonanie úlohy od skutočnej úlohy pomocou znaku Exekútor . Exekútor je zameraný na rozhranie Exekútora a má čiastkové rozhrania, t. ExecutorService a trieda ThreadPoolExecutor.
Ak teda použijeme Exekútor, musíme len vytvoriť spustiteľné objekty a potom ich poslať exekútorovi, ktorý ich vykoná.
Niektoré z najlepších postupov, ktoré je potrebné dodržiavať pri používaní rámca Executor, sú:
- Mali by sme skontrolovať a naplánovať kód tak, aby sme skontrolovali najvyššie zoznamy, aby sme v kóde mohli zistiť zablokovanie aj živé zablokovanie.
- Kód Java by sa mal vždy vykonávať proti nástrojom statickej analýzy. Príklady nástrojov statickej analýzy sú FindBugs a PMD.
- Mali by sme chytiť nielen výnimky, ale aj chyby vo viacvláknových programoch.
Teraz poďme diskutovať o komponentoch Executor Framework v Jave.
Exekútor
Exekútor možno definovať ako rozhranie používané na reprezentáciu objektu, ktorý vykonáva úlohy, ktoré sú mu poskytované. To, či sa má úloha spustiť na aktuálnom alebo novom vlákne, závisí od bodu, odkiaľ bolo vyvolané vyvolanie, čo ďalej závisí od implementácie.
Takže pomocou Exekútora môžeme oddeliť úlohy od skutočnej úlohy a potom ich spustiť asynchrónne.
Vykonanie úlohy pomocou Exekútora však nemusí byť asynchrónne. Exekútori môžu tiež úlohu vyvolať okamžite pomocou vyvolávacieho vlákna.
Nižšie je uvedený príklad kódu na vytvorenie inštancie Executor:
public class Invoker implements Executor { @Override public void execute (Runnable r_interface) { r_interface.run(); } }
Po vytvorení vyvolávača, ako je uvedené vyššie, ho môžeme použiť na vykonanie úlohy nasledujúcim spôsobom.
public void execute () { Executor executor = new Invoker (); executor.execute ( () -> { //perform task }); }
Upozorňujeme, že ak exekútor úlohu neprijme, vyhodí RejectedExecutionException.
ExecutorService
Služba ExecutorService (java.util.concurrent.ExecutorService) naplánuje zadané úlohy podľa dostupnosti vlákien a tiež udržiava frontu pamäte. ExecutorService funguje ako kompletné riešenie pre asynchrónne spracovanie úloh.
Ak chcete v kóde použiť ExecutorService, vytvoríme triedu Runnable. ExecutorService udržiava fond vlákien a tiež priraďuje úlohy vláknam. Úlohy sa môžu tiež zaradiť do poradia, ak vlákno nie je k dispozícii.
Nižšie je uvedený jednoduchý príklad 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(); } }
Výkon
Vo vyššie uvedenom programe vytvoríme jednoduchú inštanciu ExecutorService s fondom vlákien pozostávajúcim z 10 vlákien. Potom je priradený k inštancii Runnable a vykonaný na vytlačenie správy uvedenej vyššie. Po vytlačení správy sa služba ExecutorService vypne.
Pool závitov
Fond vlákien v Jave je skupina pracovných vlákien, ktoré je možné opakovane použiť opakovane a priradiť im úlohy.
Fond vlákien obsahuje skupinu vlákien pevnej veľkosti. Každé vlákno je vytiahnuté z fondu vlákien a poskytovateľ služby mu pridelí úlohu. Po dokončení priradenej úlohy sa vlákno znova pridelí oblasti vlákien.
Pool vláken je výhodný, pretože nemusíme vytvárať nové vlákno vždy, keď je k dispozícii úloha, čím sa zvyšuje výkon. Používa sa v aplikáciách v reálnom čase, ktoré používajú Servlet a JSP, kde sa na spracovanie požiadaviek používajú oblasti vlákien.
V aplikáciách s viacerými vláknami šetrí skupina vlákien zdroje a pomáha udržiavať paralelizmus v rámci vopred určených limitov.
Nižšie uvedený program Java demonštruje fond vlákien v prostredí 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'); } }
Výkon
Vo vyššie uvedených programoch existuje oblasť vlákien s 5 vláknami, ktoré sú vytvorené metódou “newFixedThreadPool”. Potom sa vlákna vytvoria a pridajú do fondu a pridelia sa ExecutorService na vykonanie.
Vyvolateľné v Jave
Už vieme, že vlákna môžeme vytvárať pomocou dvoch prístupov. Jedným z prístupov je rozšírenie triedy Thread, zatiaľ čo druhým prístupom je implementácia rozhrania Runnable.
Vlákna vytvorené pomocou rozhrania Runnable však nemajú jednu funkciu, t. J. Nevrátia výsledok, keď je vlákno ukončené alebo run () dokončí vykonávanie. Toto je miesto, kde sa do obrazu dostane rozhranie Vyvolateľné.
Pomocou rozhrania Callable definujeme úlohu tak, aby vracala výsledok. Môže to tiež spôsobiť výnimku. Rozhranie Callable je súčasťou balíka java.util.concurrent.
Rozhranie Callable poskytuje metódu call (), ktorá je na podobných linkách ako metóda run () poskytovaná rozhraním Runnable, s jediným rozdielom, že metóda call () vracia hodnotu a vyvoláva kontrolovanú výnimku.
Metóda call () volaného rozhrania má nasledujúci prototyp.
public Object call () throws Exception;
Pretože metóda call () vracia Object, musí si to hlavné vlákno uvedomiť.
Preto by návratová hodnota mala byť uložená v inom objekte známom hlavnému vláknu. Tomuto účelu slúži objekt „Budúcnosť“. Budúci objekt je objekt, ktorý obsahuje výsledok vrátený vláknom. Alebo inými slovami, bude držať výsledok, keď sa vráti Vyvolávateľná.
Callable zapuzdruje úlohu, ktorá by mala bežať v inom vlákne. Budúci objekt ukladá výsledok vrátený z iného vlákna.
Vyvolávacie rozhranie nemožno použiť na vytvorenie vlákna. Na vytvorenie vlákna potrebujeme Runnable. Na uloženie výsledku je potrebný budúci objekt. Java poskytuje konkrétny typ s názvom „FutureTask“, ktorý kombinuje funkčnosť implementáciou Runnable aj Future.
FutureTask vytvárame tak, že konštruktorovi poskytneme Callable. Tento objekt FutureTask sa potom odovzdá konštruktoru triedy Thread na vytvorenie objektu Thread.
Ďalej je uvedený program Java, ktorý demonštruje voliteľné rozhranie a objekt Future. V tomto programe používame aj objekt FutureTask.
Ako už bolo spomenuté, v programe vytvárame triedu, ktorá implementuje rozhranie Callable s metódou overridden call (). V hlavnej metóde vytvoríme 10 objektov FutureTask. Každý konštruktor objektov má ako argument objekt triedy Callable. Potom je objekt FutureTask spojený s inštanciou vlákna.
Preto nepriamo vytvárame vlákno pomocou objektu rozhrania 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() + ' '); } } }
Výkon
Ako je uvedené v predchádzajúcom programe, metóda call () Callable, ktorá je prepísaná v triede implementujúcej Callable, generuje náhodné čísla. Po spustení vlákna sa tieto náhodné čísla zobrazia.
V hlavnej funkcii tiež používame objekty FutureTask. Pretože implementuje rozhranie Future, nemusíme výsledky ukladať do objektov Thread. Podobne môžeme úlohu zrušiť, skontrolovať, či je spustená alebo dokončená, a tiež získať výsledok pomocou objektu FutureTask.
ReentrantLock v Jave
O synchronizácii vlákien pomocou synchronizovaného kľúčového slova sme sa podrobne zmienili v našom poslednom návode. Použitie synchronizovaného slova na synchronizáciu vlákien je základnou metódou a je trochu rigidné.
Pomocou synchronizovaného kľúčového slova sa vlákno môže uzamknúť iba raz. Tiež potom, čo jedno vlákno opustí synchronizovaný blok, ďalšie vlákno získa zámok. Neexistuje žiadny čakací rad. Tieto problémy môžu spôsobiť hladovanie niektorých ďalších vlákien, pretože sa môže stať, že sa dlho nedostanú k zdrojom.
Na riešenie týchto problémov potrebujeme flexibilnú metódu synchronizácie vlákien. „Reentrantné zámky“ je táto metóda v Jave, ktorá poskytuje synchronizáciu s oveľa väčšou flexibilitou.
Trieda „ReentrantLock“ implementuje zámky Reentrant a je súčasťou balíka „import java.util.concurrent.locks“. Trieda ReentrantLock poskytuje synchronizáciu metódy na prístup k zdieľaným prostriedkom. Triedy majú tiež metódy uzamknutia a odomknutia na uzamknutie / odomknutie zdrojov, keď k nim majú prístup vlákna.
Jednou zvláštnou vlastnosťou ReentrantLock je, že vlákno môže pomocou ReentrantLock uzamknúť zdieľaný prostriedok viackrát. Poskytuje počet pozdržaní, ktorý je nastavený na jeden, keď vlákno uzamkne zdroj.
Vlákno môže pred odomknutím znova vstúpiť do zdroja a získať k nemu prístup. Zakaždým, keď vlákno pristupuje k prostriedku pomocou zámku opätovného zadania, počet zadržaní sa zvýši o jednu. Pri každom odomknutí sa počet pozdržaní zníži o jednu.
Keď počet pozdržaní dosiahne 0, zdieľaný zdroj sa odomkne.
Trieda ReentrantLock tiež poskytuje parameter spravodlivosti, ktorý predstavuje logickú hodnotu, ktorú je možné odovzdať konštruktoru zámku. Keď je parameter spravodlivosti nastavený na hodnotu true, potom kedykoľvek jedno vlákno uvoľní zámok, zámok sa odovzdá najdlhšie čakajúcemu vláknu. Tým sa zabráni hladovaniu.
Zámky Reentrant je možné použiť nasledovne:
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } }
Upozorňujeme, že vyhlásenie o odomknutí pre ReentrantLock je vždy v konečne bloku. To zaručuje, že zámok sa uvoľní, aj keď dôjde k vyvolaniu výnimky.
Poďme implementovať program Java, aby sme pochopili 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(); } }
Výkon
Vo vyššie uvedenom programe sme vytvorili vlákno a použili sme ho ReentrantLock. Pomocou ReentrantLock je prístup k zdieľanému prostriedku.
Semafor v Jave
Ďalšou metódou synchronizácie vlákien je použitie Semaforu. Pomocou tohto konštruktu nazývaného semafor je prístup k zdieľanému prostriedku kontrolovaný cez počítadlo. Signály sa posielajú medzi vláknami, aby sme mohli strážiť kritický úsek a tiež sa vyhnúť zmeškaným signálom.
Semafor je možné definovať ako premennú, ktorá sa používa na správu súbežných procesov synchronizáciou týchto procesov. Semafory sa tiež používajú na synchronizáciu prístupu k zdieľanému prostriedku a tým sa vyhnú podmienkam rasy. Povolenie dané vláknu na prístup k zdieľanému prostriedku semaforom sa tiež nazýva povolenie.
Podľa toho, aké funkcie vykonávajú, je možné semafory rozdeliť do dvoch typov:
# 1) Binárny semafor: Binárny semafor sa používa na synchronizáciu súbežných procesov a implementáciu vzájomného vylúčenia. Binárny semafor predpokladá iba dve hodnoty, tj 0 a 1.
# 2) Počítanie semaforu: Počítací semafor má hodnotu, ktorá udáva počet procesov, ktoré môžu vstúpiť do kritickej sekcie. Hodnota v ktoromkoľvek okamihu označuje maximálny počet procesov, ktoré vstupujú do kritickej sekcie.
Ako teda semafor funguje?
Fungovanie semaforu možno zhrnúť do nasledujúcich krokov:
- Ak je počet semaforov> 0, znamená to, že vlákno má povolenie na prístup ku kritickej sekcii a potom je počet znížený.
- V opačnom prípade je vlákno blokované, kým nezískate povolenie.
- Keď je vlákno hotové s prístupom k zdieľanému prostriedku, povolenie je uvoľnené a počet semaforov je zvýšený, takže iné vlákno môže opakovať vyššie uvedené kroky a získať povolenie.
Vyššie uvedené kroky spracovania semaforov je možné zhrnúť v nasledujúcom vývojovom diagrame.
V prostredí Java nemusíme implementovať náš semafor, ale poskytuje a Semafor trieda, ktorá implementuje funkčnosť semaforu. Trieda Semafor je súčasťou java.util.concurrent balíček.
Trieda Semafor poskytuje nasledujúce konštruktory, pomocou ktorých môžeme vytvárať semaforový objekt:
Semaphore (int num_value) Semaphore (int num_value, boolean how)
Tu,
num_value => počiatočná hodnota počtu povolení, ktorá určuje počet vlákien, ktoré majú prístup k zdieľanému prostriedku.
ako => nastavuje poradie, v akom budú vlákna pridelené povolenia (how = true). Ak how = false, potom sa takéto poradie nedodržiava.
Teraz implementujeme program Java, ktorý predvedie Semafor, ktorý sa používa na správu prístupu k zdieľaným prostriedkom a na zabránenie stavu rasy.
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. } }
Výkon
Tento program vyhlásil triedu pre zdieľaný prostriedok. Deklaruje tiež triedu vlákna, v ktorej máme semaforovú premennú, ktorá je inicializovaná v konštruktore triedy.
V metóde overridden run () triedy Thread sa spracováva inštancia vlákna, v ktorej vlákno získa povolenie, získa prístup k zdieľanému prostriedku a potom povolenie uvoľní.
V hlavnej metóde sme deklarovali dve inštancie vlákna. Obe vlákna sa potom spustia a potom čakajú pomocou metódy spojenia. Nakoniec sa zobrazí počet, tj. 0, čo naznačuje, že obe vlákna skončili so zdieľaným prostriedkom.
Vidlica a pripojenie sa v Jave
Rámec fork / join bol prvýkrát predstavený v prostredí Java 7. Tento rámec pozostáva z nástrojov, ktoré môžu urýchliť paralelné spracovanie. Využíva všetky dostupné procesorové jadrá v systéme a dokončí úlohu. Rámec fork / join využíva prístup rozdelenia a panovania.
Základnou myšlienkou rámca Fork / Join je, že prvý rámec „Forks“, tj. Rekurzívne rozdeľuje úlohu na menšie jednotlivé podúlohy, kým úlohy nie sú atómové, aby ich bolo možné vykonať asynchrónne.
Potom sa úlohy „spoja“, t. J. Všetky podúlohy sa rekurzívne spoja do jednej úlohy alebo návratovej hodnoty.
Rámec fork / join má skupinu vlákien známych ako „ForkJoinPool“. Táto oblasť spravuje typ pracovných vlákien „ForkJoinWorkerThread“, čím poskytuje efektívne paralelné spracovanie.
ForkJoinPool spravuje pracovné vlákna a tiež nám pomáha získať informácie týkajúce sa výkonu a stavu fondu vlákien. ForkJoinPool je implementácia služby „ExecutorService“, o ktorej sme hovorili vyššie.
Na rozdiel od pracovných vlákien ForkJoinPool nevytvára samostatné vlákno pre každú podúlohu. Každé vlákno vo ForkJoinPool si zachováva svoju deque (dvojitá fronta) na ukladanie úloh.
Deque funguje ako vyvažovanie pracovného zaťaženia vlákna a robí to pomocou „algoritmu odcudzenia práce“, ktorý je popísaný nižšie.
Algoritmus kradnutia práce
Algoritmus kradnutia práce môžeme definovať jednoduchými slovami ako „Ak je vlákno voľné,„ ukradnite “prácu rušným vláknam“.
Pracovné vlákno bude vždy dostávať úlohy zo svojho archívu. Keď sú všetky úlohy v deque vyčerpané a deque je prázdny, pracovné vlákno vezme úlohu z chvosta iného deque alebo z „globálneho vstupného frontu“.
Týmto spôsobom je minimalizovaná možnosť vlákien súťažiť o úlohy a tiež je znížený počet prípadov, kedy vlákno musí hľadať prácu. Je to tak preto, lebo vlákno už získalo najväčší balík dostupnej práce a dokončilo ho.
Ako teda môžeme použiť ForkJoinPool v programe?
Všeobecná definícia ForkJoinPool je nasledovná:
public class ForkJoinPool extends AbstractExecutorService
Trieda ForkJoinPool je súčasťou balíka „java.util.concurrent“.
V prostredí Java 8 vytvoríme inštanciu ForkJoinPool pomocou jej statickej metódy „common-pool ()“, ktorá poskytuje odkaz na spoločný fond alebo predvolený fond vlákien.
ForkJoinPool commonPool = ForkJoinPool.commonPool ();
V prostredí Java 7 vytvoríme inštanciu ForkJoinPool a priradíme ju k poľu triedy nástrojov, ako je uvedené nižšie.
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
Vyššie uvedená definícia naznačuje, že fond má úroveň paralelizmu 2, takže fond bude používať 2 procesorové jadrá.
Pre prístup k vyššie uvedenému fondu môžeme uviesť nasledujúce vyhlásenie.
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
Základný typ pre úlohy ForkJoinPool je „ForkJoinTask“. Mali by sme rozšíriť jednu z jeho podtried, t. J. Pre neplatné úlohy RecursiveAction a pre úlohy vracajúce hodnotu RecursiveTask. Obe rozšírené triedy poskytujú abstraktnú metódu výpočtu (), v ktorej definujeme logiku úlohy.
Nižšie je uvedený príklad na demonštráciu ForkJoinPool.
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()); } }
Výkon
Vo vyššie uvedenom programe nájdeme počet aktívnych vlákien v systéme pred a po volaní metódy „invoke ()“. Na odoslanie úloh do fondu sa používa metóda invoke (). Nájdeme tiež počet dostupných jadier procesora v systéme.
často kladené otázky
Otázka 1) Čo je Java Util Concurrent?
Odpoveď: Balík „java.util.concurrent“ je sada tried a rozhraní poskytovaných programom Java na uľahčenie vývoja súbežných (viacvláknových) aplikácií. Pomocou tohto balíka môžeme priamo používať rozhranie a triedy, ako aj API bez toho, aby sme museli písať naše triedy.
Otázka č. 2) Ktoré z nasledujúcich súbežných implementácií prítomných v súbore java.util. súbežný balík?
Odpoveď: Na vysokej úrovni obsahuje balík java.util.concurrent obslužné programy ako Exekútori, Synchronizátory, Fronty, Časovanie a Súbežné zbierky.
Otázka č. 3) Čo je to budúca Java?
Odpoveď: Budúci objekt (java.util.concurrent.Future) sa používa na ukladanie výsledku vráteného vláknom pri implementácii rozhrania Callable.
Otázka č. 4) Čo je bezpečné pre vlákna v Jave?
Odpoveď: Kód alebo trieda bezpečná pre vlákna v Jave je kód alebo trieda, ktorú je možné zdieľať bez problémov v prostredí s viacerými vláknami alebo v súbežnom prostredí a prináša očakávané výsledky.
čo je súbor eps?
Otázka č. 5) Čo je synchronizovaná zbierka v Jave?
Odpoveď: Synchronizovaná kolekcia je kolekcia bezpečná pre vlákna. Metóda synchronized collection () triedy java.util.Collections vracia synchronizovanú (bezpečnú pre vlákna) kolekciu.
Záver
Pomocou tohto tutoriálu sme dokončili tému multi-threading a concurrency v Jave. Multithreadingu sme sa podrobne venovali v našich predchádzajúcich tutoriáloch. Tu sme diskutovali o súbežnosti a implementácii súvisiacej so súbežnosťou a multithreadingom, ktoré sú súčasťou balíka java.util.concurrent.
Diskutovali sme o ďalších dvoch synchronizačných metódach, semaforoch a ReentrantLock. Diskutovali sme tiež o ForkJoinPool, ktorý sa používa na vykonávanie úloh, ich rozdelením na jednoduchšie úlohy a následným pripojením sa k výsledku.
Balík java.util.concurrent podporuje aj rámec Executor a exekútorov, ktorí nám pomáhajú vykonávať vlákna. Diskutovali sme tiež o implementácii fondu vlákien, ktorá pozostáva z opakovane použiteľných vlákien, ktoré sa vrátia do fondu po dokončení vykonávania.
Diskutovali sme o ďalšom rozhraní podobnom Runnable, ktoré nám tiež pomáha vrátiť výsledok z vlákna a objektu Future použitého na uloženie získaného výsledku vlákna.
=> Dajte si pozor na jednoduchú sériu školení Java tu.
Odporúčané čítanie
- Thread.Sleep () - Metóda Thread Sleep () v Jave s príkladmi
- Nasadenie Java: Vytvorenie a vykonanie súboru Java JAR
- Základy jazyka Java: Java Syntax, trieda Java a základné koncepty Java
- Virtuálny stroj Java: Ako JVM pomáha pri spúšťaní aplikácií Java
- Modifikátory prístupu v prostredí Java - návod s príkladmi
- Synchronizovaná Java: Čo je synchronizácia vlákna v prostredí Java
- Výukový program JAVA pre začiatočníkov: viac ako 100 praktických výučbových programov Java Video
- Celé číslo Java a trieda Java BigInteger s príkladmi