Java Concurrence: Cadrul de execuție

Introducere

cu creșterea numărului de nuclee disponibile în procesoare astăzi în zi, de-a lungul zilei Odată cu creșterea necesității de a obține o performanță mai mare, API-urile multi-filetate devin destul de populare. Java oferă propriul cadru multi-filet numit cadru executor.

Ce este executorul-cadru?

executor-cadru conține o serie de componente care sunt utilizate pentru a gestiona eficient firele de lucru. API-ul executorului decuplează executarea sarcinii de sarcină reală care va fi executată de Executors. Acest design este unul dintre implementările modelului producător-consumatorului.

java.util.concurrent.Executors Furnizați metode din fabrică care sunt utilizate pentru a crea ThreadPools de fire de lucru.

Pentru a utiliza cadrul Executor, trebuie să creăm una dintre acele subproceses grupuri și să trimitem sarcina de execuție. Este activitatea programului-cadru executor și a executa sarcinile trimise și returnate rezultatele grupului de subproces.

O întrebare de bază care vine în minte este de ce avem nevoie de astfel de grupuri de subprocese atunci când putem crea obiecte de java.lang.Thread 131d921393 „> / interfețe pentru a obține paralelismul

Răspunsul este redus la două fapte de bază:

  • Crearea unui fir nou pentru o nouă sarcină implică o supraîncărcare a creației și dezasamblate a firului. Gestionarea ciclului de viață al acestui fir crește în mod semnificativ timpul de execuție.
  • Adăugați un fir nou pentru fiecare proces fără nici un fel de limitare duce la crearea unui număr mare de fire. Aceste subprocese ocupă memoria și provoacă resurse de deșeuri. CPU-ul începe să dedice prea mult timp pentru a schimba contextul atunci când fiecare fir este schimbat și un alt fir este introdus pentru execuție.

Toți acești factori reduc performanța sistemului. Grupurile de subprocesare depășesc această problemă prin păstrarea în viață a firelor și prin reutilizarea acestora. Orice sarcini excesive care curg din ce subprocese din grup se pot ocupa sunt păstrate într-un Queue. Odată ce oricare dintre fire sunt lansate, ele preiau următoarea sarcină a acestei coadă. Această coadă de sarcini este, în esență, nelimitată pentru executorii gata să utilizeze furnizați de JDK.

Tipuri de Executori

Acum că avem o idee bună despre ceea ce este un executor, să fie Luați o privire, de asemenea, la diferitele tipuri de executorii.

SingleTadexCeactor

Acest executor al grupului subprocess are doar un fir. Este folosit pentru a executa sarcini secvențial. Dacă firul moare din cauza unei excepții în timp ce rulează o sarcină, este creat un nou fir pentru a înlocui firele vechi și sarcinile ulterioare sunt executate în noul.

ExecutorService executorService = Executors.newSingleThreadExecutor()

FIVETHREDPOOL (N)

Numele său indică, este un grup de subprocese de un număr fix de subprocese. Sarcinile trimise executorului sunt executate de firele n și dacă există mai multe sarcini sunt stocate într-un LinkedBlockingQueue. Acest număr este, de obicei, numărul total de subprocese suportate de procesorul subiacent.

ExecutorService executorService = Executors.newFixedThreadPool(4);

cachedthreadpool

Acest grup de subprocese este utilizat în principal când Există multe sarcini paralele de scurtă durată pentru a executa. Spre deosebire de grupul de fire fixe, numărul de subprocese din acest grup de executorii nu este limitat. Dacă toate firele sunt ocupate cu efectuarea unor sarcini și una nouă, grupul va crea și va adăuga un nou subproces la Executor. De îndată ce una dintre subprocese este gratuită, va relua executarea noilor sarcini. Dacă un fir rămâne inactiv timp de șaizeci de secunde, acestea sunt finisate și scoase din memoria cache.

Cu toate acestea, dacă nu este administrată corect sau sarcinile nu sunt de scurtă durată, grupul de subprocese va avea multe active subprocese. Acest lucru poate duce la eliminarea resurselor și, prin urmare, la o scădere a performanței

ExecutorService executorService = Executors.newCachedThreadPool();

programat fiscal

Acest executor este folosit atunci când noi au o sarcină care trebuie să fie executată la intervale regulate sau dacă vrem să întârziem o anumită sarcină.

ScheduledExecutorService scheduledExecService = Executors.newScheduledThreadPool(1);

Sarcinile pot fi programate în ScheduledExecutor folosind oricare dintre cele două metode scheduleAtFixedRate sau scheduleWithFixedDelay.

divid id =” 957762D742 ” >

scheduledExecService.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)

Principala diferență dintre cele două metode este interpretarea îndeaproape a întârzierii dintre execuțiile consecutive ale lucrărilor programate.

scheduleAtFixedRate execută sarcina cu un interval fix, indiferent când sarcina anterioară sa terminat.

scheduleWithFixedDelay va începe numărătoarea întârzierii numai după finalizarea sarcinii curente.

Înțelegerea obiectului viitor

Puteți accesa rezultatul sarcinii trimise pentru execuție la un Executor utilizând obiectul java.util.concurrent.Future returnat de executor. Viitorul poate fi considerat o promisiune făcută de executor la apelantul.

divid id = „152159CD84”>

O sarcină trimisă către executor, ca cea anterioară, este asincronă, Adică, execuția programului nu așteaptă executarea sarcinii de a se deplasa la pasul următor. În schimb, ori de câte ori execuția sarcinii este finalizată, este stabilită în acest obiect Future de către Albacea.

Apelantul poate continua să ruleze programul principal și Când este necesar rezultatul sarcinii trimise, puteți apela .get() în acest obiect Future. Dacă sarcina este finalizată, rezultatul este returnat imediat la apelant sau, altfel, apelantul este blocat până când executantul completează execuția și rezultatul este calculat.

dacă persoana care cheamă nu își poate permite o perioadă nedeterminată înainte Recuperarea rezultatului, această așteptare poate fi, de asemenea, programată. Acest lucru se realizează prin metoda Future.get(long timeout, TimeUnit unit) care produce un TimeoutException dacă rezultatul nu este returnat în perioada stipulată. Apelantul poate gestiona această excepție și continuă cu executarea ulterioară a programului.

Dacă există o excepție atunci când executați sarcina, metoda GET va da un ExecutionException

Ceva important despre rezultatul returnat de metoda este faptul că este returnată numai dacă sarcina a fost trimisă implementează java.util.concurrent.Callable Dacă sarcina implementează interfața Runnable, apelul către .get() va returna null odată Sarcina este completă.

O altă metodă importantă este metoda . Această metodă este utilizată pentru a anula executarea unei sarcini trimise. Dacă sarcina este deja în funcțiune, Executorul va încerca să întrerupă executarea sarcinii dacă mayInterruptIfRunning Steagul este trecut ca true.

exemplu: Creați și executați un executor simplu

Acum vom crea o sarcină și vom încerca să o execut într-un executor de grup fix:

public class Task implements Callable<String> { private String message; public Task(String message) { this.message = message; } @Override public String call() throws Exception { return "Hello " + message + "!"; }}

Task Clasa implementează Callable și este parametrizată pentru String Tip. De asemenea, este declarată prin aruncarea Exception. Această abilitate de a lansa o excepție de la Executor și executorul care returnează această excepție la apelant este de mare importanță deoarece ajută persoana să cheme să cunoască starea de execuție a sarcinii.

Acum executăm această sarcină:

public class ExecutorExample { public static void main(String args) { Task task = new Task("World"); ExecutorService executorService = Executors.newFixedThreadPool(4); Future<String> result = executorService.submit(task); try { System.out.println(result.get()); } catch (InterruptedException | ExecutionException e) { System.out.println("Error occured while executing the submitted task"); e.printStackTrace(); } executorService.shutdown(); }}

Aici am creat un executor FixedThreadPool, cu un număr de 4 fire, deoarece această demonstrație este dezvoltată într-o Procesor cu patru nuclee. Numărul de subprocese poate fi mai mare decât miezurile procesorului dacă sarcinile care sunt executate efectuează o operație considerabilă I / O sau petrec timpul de așteptare a resurselor externe.

Am instaniat Task Clasa și luați-o către Executor pentru execuție. Rezultatul este returnat de obiectul Future, care se imprimă apoi pe ecran.

Lăsați-ne să rulăm ExecutorExample și verificați ieșirea:

Hello World!

După cum era de așteptat, sarcina adaugă salutul „salut” și returnează rezultatul prin Future Obiect.

În cele din urmă, sunăm la momentul obiectului executorService pentru a termina toate firele și pentru a returna resursele la sistemul de operare.

.shutdown() Metoda este în prezent finalizarea sarcinilor trimise executorului. Cu toate acestea, dacă cerința închide imediat executorul fără a aștepta, putem folosi .shutdownNow() în loc.

Orice sarcină de execuție în așteptare va fi returnată într-un obiect java.util.List.

De asemenea, putem crea aceeași sarcină prin implementarea interfeței Runnable:

public class Task implements Runnable{ private String message; public Task(String message) { this.message = message; } public void run() { System.out.println("Hello " + message + "!"); }}

există o pereche De schimbări importante aici când implementăm runnable.

  • Rezultatul executării sarcinii nu poate fi returnat din metoda . Prin urmare, imprimăm direct de aici.
  • run() Metoda nu este configurată pentru a lansa orice excepție marcată.

Concluzie

Subprocesul multiplu devine din ce în ce mai frecvent, deoarece viteza ceasului procesorului este dificil de crescut. Cu toate acestea, manipularea ciclului de viață al fiecărui fir este foarte dificil datorită complexității implicate.

În acest articol, am demonstrat un cadru multiplu multiplu, dar simplu, un cadru executor și am explicat diferitele sale componente. De asemenea, aruncăm o privire la exemple diferite de creare și execuție a sarcinilor la un executor.

Ca întotdeauna, codul acestui exemplu poate fi găsit în GitHub.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *