Pregled java.util.concurrent

1. Pregled

The java.util.concurrent paket pruža alate za stvaranje istodobnih aplikacija.

U ovom ćemo članku napraviti pregled cijelog paketa.

2. Glavne komponente

The java.util.concurrent sadrži previše značajki za raspravu u jednom zapisu. U ovom ćemo se članku uglavnom usredotočiti na neke od najkorisnijih uslužnih programa iz ovog paketa, poput:

  • Izvršitelj
  • ExecutorService
  • ScheduledExecutorService
  • Budućnost
  • CountDownLatch
  • CyclicBarrier
  • Semafor
  • ThreadFactory
  • BlockingQueue
  • DelayQueue
  • Brave
  • Phaser

Ovdje možete pronaći i mnogo posvećenih članaka za pojedine razrede.

2.1. Izvršitelj

Izvršitelj je sučelje koje predstavlja objekt koji izvršava zadate zadatke.

Ovisno o određenoj implementaciji (odakle se pokreće poziv) treba li zadatak izvršiti na novoj ili trenutnoj niti. Stoga, koristeći ovo sučelje, možemo razdvojiti tijek izvršavanja zadatka od stvarnog mehanizma izvršenja zadatka.

Ovdje valja primijetiti jednu stvar Izvršitelj ne zahtijeva strogo izvršavanje zadatka da bude asinkrono. U najjednostavnijem slučaju, izvršitelj može podnijeti poslani zadatak odmah u niti poziva.

Moramo stvoriti pozivač za stvaranje egzekutorske instance:

javna klasa Invoker implementira izvršitelj {@Override public void execute (Runnable r) {r.run (); }}

Sada možemo koristiti ovaj prizivač za izvršavanje zadatka.

public void execute () {Izvršitelj izvršitelj = novi Invoker (); executor.execute (() -> {// zadatak koji treba izvršiti}); }

Ovdje valja napomenuti da ako izvršitelj ne može prihvatiti zadatak za izvršenje, bacit će RejectedExecutionException.

2.2. ExecutorService

ExecutorService je cjelovito rješenje za asinkronu obradu. Upravlja redom u memoriji i raspoređuje predane zadatke na temelju dostupnosti niti.

Koristiti ExecutorService, moramo ga stvoriti Izvodljivo razred.

javna klasa Zadatak implementira pokrenuti {@Override public void run () {// detalji zadatka}}

Sada možemo stvoriti ExecutorService instance i dodijelite ovaj zadatak. U vrijeme izrade moramo odrediti veličinu spremišta niti.

ExecutorService izvršitelj = Executors.newFixedThreadPool (10);

Ako želimo stvoriti jednonitni ExecutorService primjerice, možemo koristiti newSingleThreadExecutor (ThreadFactory threadFactory) za stvaranje instance.

Nakon što se izvršilac stvori, možemo ga koristiti za predaju zadatka.

javna praznina execute () {executor.submit (novi zadatak ()); }

Također možemo stvoriti Izvodljivo instance tijekom predaje zadatka.

izvršitelj.submit (() -> {novi zadatak ();});

Također dolazi s dvije gotove metode prekida izvršenja. Prva je ugasiti(); čeka dok se svi predani zadaci ne izvrše. Druga metoda je shutdownNow () kojih odmah prekida sve zadatke na čekanju / izvršavanju.

Postoji i druga metoda awaitTermination (dugo vrijeme čekanja, jedinica vremenske jedinice) koji prisilno blokira dok svi zadaci ne dovrše izvršenje nakon što se pokrene događaj isključivanja ili se dogodi vremensko ograničenje izvršenja ili dok se sama nit izvršenja ne prekine,

pokušajte {executor.awaitTermination (20l, TimeUnit.NANOSECONDS); } catch (InterruptedException e) {e.printStackTrace (); }

2.3. ScheduledExecutorService

ScheduledExecutorService je slično sučelje ExecutorService, ali može povremeno izvršavati zadatke.

Izvršitelj i izvršitelj uslugeMetode su zakazane na licu mjesta bez uvođenja umjetnog odgađanja. Nula ili bilo koja negativna vrijednost znači da zahtjev treba izvršiti odmah.

Možemo koristiti oboje Izvodljivo i Pozivno sučelje za definiranje zadatka.

javna void izvršiti () {ScheduledExecutorService izvršiteljService = Izvršitelji.newSingleThreadScheduledExecutor (); Buduća budućnost = executorService.schedule (() -> {// ... return "Hello world";}, 1, TimeUnit.SECONDS); ScheduledFuture rasporedFuture = executorService.schedule (() -> {// ...}, 1, TimeUnit.SECONDS); executorService.shutdown (); }

ScheduledExecutorService također može zakazati zadatak nakon određenog fiksnog kašnjenja:

executorService.scheduleAtFixedRate (() -> {// ...}, 1, 10, TimeUnit.SECONDS); executorService.scheduleWithFixedDelay (() -> {// ...}, 1, 10, TimeUnit.SECONDS);

Evo, scheduleAtFixedRate (naredba za izvođenje, dugo početno odgađanje, dugo razdoblje, jedinica vremenske jedinice) metoda stvara i izvršava periodičnu radnju koja se poziva prvo nakon predviđenog početnog kašnjenja, a zatim s danim razdobljem do isključenja instance usluge.

The scheduleWithFixedDelay (naredba za izvođenje, dugo početno odgađanje, dugo kašnjenje, jedinica vremenske jedinice) metoda stvara i izvršava periodičnu radnju koja se poziva prvo nakon predviđenog početnog kašnjenja, i više puta s danim kašnjenjem između završetka izvršnog i pozivanja sljedećeg.

2.4. Budućnost

Budućnost koristi se za predstavljanje rezultata asinkrone operacije. Dolazi s metodama za provjeru je li asinhrona operacija dovršena ili nije, dobivanje izračunatog rezultata itd.

Štoviše, poništi (logička vrijednost možeInterruptIfRunning) API otkazuje operaciju i oslobađa izvršnu nit. Ako je vrijednost od svibanjInterruptIfRunning je istina, nit koja izvršava zadatak odmah će se prekinuti.

Inače, zadaci u tijeku moći će se dovršiti.

Ispod isječka koda možemo koristiti za stvaranje buduće instance:

javni void invoke () {ExecutorService executorService = Izvršitelji.newFixedThreadPool (10); Buduća budućnost = executorService.submit (() -> {// ... Thread.sleep (10000l); return "Hello world";}); }

Sljedećim isječkom koda možemo provjeriti je li budući rezultat spreman i dohvatiti podatke ako je izračunavanje završeno:

if (future.isDone () &&! future.isCancelled ()) {try {str = future.get (); } catch (InterruptedException | ExecutionException e) {e.printStackTrace (); }}

Također možemo odrediti vremensko ograničenje za datu operaciju. Ako zadatak traje više od ovog vremena, a TimeoutException bačeno je:

isprobajte {future.get (10, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) {e.printStackTrace (); }

2.5. CountDownLatch

CountDownLatch (uvedeno u JDK 5) je klasa uslužnih programa koja blokira skup niti dok se neka operacija ne dovrši.

A CountDownLatch inicijalizira se s brojač (Integer tip); ovaj brojač se smanjuje kao dovršavanje izvršenja ovisnih niti. Ali kad brojač dosegne nulu, otpuštaju se druge niti.

Možete saznati više o CountDownLatch ovdje.

2.6. CyclicBarrier

CyclicBarrier radi gotovo isto kao i CountDownLatch osim što ga možemo ponovno upotrijebiti. Za razliku od CountDownLatch, omogućuje više niti da čekaju jedna drugu koristeći se čekati() metoda (poznata kao barijerni uvjet) prije pozivanja završnog zadatka.

Moramo stvoriti Izvodljivo instanca zadatka za pokretanje uvjeta zapreke:

zadaća javne klase implementira Runnable {private CyclicBarrier barrier; javni zadatak (prepreka CyclicBarrier) {this.barrier = barrier; } @Override public void run () {try {LOG.info (Thread.currentThread (). GetName () + "čeka"); barrier.await (); LOG.info (Thread.currentThread (). GetName () + "je pušten"); } catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace (); }}}

Sada možemo prizvati neke niti kako bismo se utrkivali za stanje barijere:

javni void start () {CyclicBarrier cyclicBarrier = novi CyclicBarrier (3, () -> {// ... LOG.info ("Svi prethodni zadaci su dovršeni");}); Navoj t1 = novi navoj (novi zadatak (cyclicBarrier), "T1"); Navoj t2 = novi navoj (novi zadatak (cyclicBarrier), "T2"); Navoj t3 = novi navoj (novi zadatak (cyclicBarrier), "T3"); if (! cyclicBarrier.isBroken ()) {t1.start (); t2.start (); t3.start (); }}

Evo, slomljeno je() metoda provjerava je li neka od niti prekinuta tijekom vremena izvršavanja. Uvijek bismo trebali izvršiti ovu provjeru prije izvođenja stvarnog postupka.

2.7. Semafor

The Semafor koristi se za blokiranje pristupa razini razine niti nekom dijelu fizičkog ili logičkog resursa. Semafor sadrži skup dozvola; kad god nit pokuša ući u kritični odjeljak, mora provjeriti semafor je li dozvola dostupna ili ne.

Ako dozvola nije dostupna (putem tryAcquire ()), nit ne smije uskočiti u kritični presjek; međutim, ako je dozvola dostupna, pristup se odobrava, a brojač dozvola smanjuje.

Jednom kada izvršna nit otpusti kritični odjeljak, opet se povećava brojač dozvola (radi: otpustiti () metoda).

Vremensko ograničenje za stjecanje pristupa možemo odrediti pomoću tryAcquire (dugo čekanje, jedinica vremenske jedinice) metoda.

Također možemo provjeriti broj dostupnih dozvola ili broj niti koje čekaju na stjecanje semafora.

Sljedeći isječak koda može se koristiti za implementaciju semafora:

statički semafor semafor = novi semafor (10); javna void execute () baca InterruptedException {LOG.info ("Dostupna dozvola:" + semafor.availablePermissions ()); LOG.info ("Broj niti koje čekaju preuzimanje:" + semaphore.getQueueLength ()); if (semaphore.tryAcquire ()) {try {// ...} konačno {semaphore.release (); }}}

Možemo primijeniti a Mutex poput strukture podataka pomoću Semafor. Više detalja o tome možete pronaći ovdje.

2.8. ThreadFactory

Kao što i samo ime govori, ThreadFactory djeluje kao spremište niti (nepostojeće) koje stvara novu nit na zahtjev. Eliminira potrebu za velikim brojem kodiranja za primjenu učinkovitih mehanizama stvaranja niti.

Možemo definirati a ThreadFactory:

javna klasa BaeldungThreadFactory implementira ThreadFactory {private int threadId; privatni naziv niza; javna BaeldungThreadFactory (naziv niza) {threadId = 1; this.name = ime; } @Override public Thread newThread (Runnable r) {Thread t = new Thread (r, name + "-Thread_" + threadId); LOG.info ("stvorio novu nit s id:" + threadId + "i imenom:" + t.getName ()); threadId ++; povrat t; }}

Možemo ovo iskoristiti newThread (pokrenut r) metoda za stvaranje nove niti za vrijeme izvođenja:

BaeldungThreadFactory tvornica = nova BaeldungThreadFactory ("BaeldungThreadFactory"); for (int i = 0; i <10; i ++) {Tema t = factory.newThread (novi zadatak ()); t.start (); }

2.9. BlockingQueue

U asinkronom programiranju jedan od najčešćih obrazaca integracije je obrazac proizvođač-potrošač. The java.util.concurrent paket dolazi sa strukturom podataka poznatom kao BlockingQueue - što može biti vrlo korisno u ovim asinkranim scenarijima.

Više informacija i radni primjer o tome dostupni su ovdje.

2.10. DelayQueue

DelayQueue je beskonačni blok blokirajućih elemenata u kojem se element može povući samo ako je dovršeno vrijeme isteka (poznato kao korisnički definirano kašnjenje). Dakle, najviši element (glava) imat će najviše kašnjenja u iznosu i bit će zadnja.

Više informacija i radni primjer o tome dostupni su ovdje.

2.11. Brave

Nije iznenađujuće, Zaključaj je uslužni program za blokiranje pristupa drugim nitima određenom segmentu koda, osim niti koja ga trenutno izvršava.

Glavna razlika između zaključavanja i sinkroniziranog bloka je u tome što je sinkronizirani blok u potpunosti sadržan u metodi; međutim, Lock API može imati zaključavanje () i otključavanje () u zasebnim metodama.

Više informacija i radni primjer o tome dostupni su ovdje.

2.12. Phaser

Phaser je fleksibilnije rješenje od CyclicBarrier i CountDownLatch - koristi se kao prepreka za višekratnu upotrebu na kojoj dinamički broj niti treba pričekati prije nastavka izvođenja. Možemo koordinirati više faza izvršenja, ponovnom upotrebom a Phaser primjer za svaku programsku fazu.

Više informacija i radni primjer o tome dostupni su ovdje.

3. Zaključak

U ovom preglednom članku na visokoj razini usredotočili smo se na različite dostupne uslužne programe java.util.concurrent paket.

Kao i uvijek, puni izvorni kod dostupan je na GitHub-u.