4 Metode pentru scrierea codului multi-thread în Java

4 Metode pentru scrierea codului multi-thread în Java / Programare

Multi-threading este o metodă de scriere a codului pentru executarea sarcinilor în paralel. Java a avut un suport excelent pentru scrierea codului multi-threaded încă din primele zile ale Java 1.0. Noile îmbunătățiri aduse Java au sporit modurile în care codul poate fi structurat pentru a încorpora multi-threading în programele Java.

În acest articol, comparăm câteva dintre aceste opțiuni, astfel încât să puteți judeca mai bine ce opțiune să utilizați pentru următorul proiect Java GitHub de dragoste Java? 4 motive pentru care ar trebui să găzduiți codul dvs. pe BitBucket Dragoste GitHub? 4 motive pentru care trebuie să vă găzduiți codul pe BitBucket Trebuie să vă gândiți la locul în care intenționați să stocați codul. Probabil ați auzit de GitHub. Nu este surprinzător. GitHub este utilizat de către persoane fizice și întreprinderi pentru a găzdui codul, să colaboreze la documentația ... Citeste mai mult t.

Metoda 1: Extinderea clasei Thread

Java oferă o Fir clasa care poate fi extinsă pentru a implementa alerga() metodă. Această metodă run () este locul în care implementați sarcina. Când doriți să lansați sarcina în firul propriu, puteți să creați o instanță a acestei clase și să o invocați start() metodă. Aceasta începe execuția firului și se execută până la finalizare (sau se termină într-o excepție).

Aici este o clasă simplă de Thread care doarme pentru un anumit interval ca o modalitate de simulare a unei operațiuni de lungă durată.

clasa publica MyThread extinde Thread private int sleepFor; public MyThread (int sleepFor) this.sleepFor = sleepFor;  @Override public void run () System.out.printf ("[% s] fir de pornire \ n", Thread.currentThread (). ToString ()); încercați Thread.sleep (this.sleepFor);  captură (InterruptedException ex)  System.out.printf ("[% s] sfârșind firul \ n", Thread.currentThread (). toString ());  

Creați o instanță a acestei clase de fișiere, dându-i numărul de milisecunde de somn.

Lucrător MyThread = MyThread nou (sleepFor); 

Începeți execuția acestui fir de lucrător prin invocarea metodei sale start (). Această metodă readuce controlul imediat la apelant, fără a aștepta terminarea firului.

worker.start (); System.out.printf ("[% s] thread principal \ n", Thread.currentThread (). ToString ()); 

Și aici este ieșirea din rularea acestui cod. Aceasta indică faptul că diagnosticul principal al firului este imprimat înainte ca firul lucrătorului să fie executat.

[Thread [main, 5, main]] thread principal [Filet [Thread-0,5, main]] pornire filet [Thread [0,5, main] 

Întrucât nu mai există declarații după pornirea firului muncitor, firul principal așteaptă ca firul lucrătorului să termine înainte de ieșirea programului. Acest lucru permite firului lucrătorului să-și finalizeze sarcina.

Metoda 2: Utilizarea unei instanțe a unui fir cu o executare

Java oferă, de asemenea, o interfață numită runnable care pot fi implementate de o clasă muncitoare pentru a executa sarcina în cadrul acesteia alerga() metodă. Acesta este un mod alternativ de a crea o clasă muncitoare, spre deosebire de extinderea acesteia Fir clasa (descrisă mai sus).

Aici este implementarea clasei de muncitori care implementează acum Runnable în loc de a extinde Thread.

clasa publică MyThread2 implementează Runnable // aceeași ca mai sus 

Avantajul implementării interfeței Runnable în locul extinderii clasei Thread este că clasa lucrător poate extinde acum o clasă specifică domeniului într-o ierarhie de clasă.

Ce inseamna asta?

Să spunem, de exemplu, că aveți un a fruct clasa care implementează anumite caracteristici generice ale fructelor. Acum doriți să implementați a Papaya care specializează anumite caracteristici ale fructelor. Puteți face asta prin a avea Papaya clasa extinde fruct clasă.

clasa publica Fruit // fruct specifica aici clasa publica Papaya extinde Fruit // suprascrie comportamentul specific papaya aici 

Acum, să presupunem că aveți o sarcină consumatoare de timp pe care Papaya trebuie să o susțină, care poate fi realizată într-un fir separat. Acest caz poate fi manipulat prin folosirea aplicației de clasă Papaya Runnable și furnizarea metodei run () unde se efectuează această activitate.

clasa publică Papaya extinde aplicațiile Fruit Runnable // suprascrie comportamentul specific papaya aici @Override public void run () // sarcina consumatoare de timp aici.  

Pentru a da startul firului lucrător, creați o instanță a clasei lucrătorilor și predați-o unei instanțe Thread la creație. Când se invocă metoda start () a Threadului, sarcina se execută într-un fir separat.

Papaya papaya = papaya nouă (); // setați proprietățile și invoca metodele de papaya aici. Thread Thread = Subiect nou (papaya); thread.start (); 

Și acesta este un rezumat succint al modului în care se poate utiliza un Runnable pentru a implementa o sarcină executată într-un fir.

Metoda 3: Executați o Executare cu ExecutorService

Începând cu versiunea 1.5, Java oferă un ExecutorService ca o nouă paradigmă pentru crearea și gestionarea firelor în cadrul unui program. Acesta generalizează conceptul de execuție a firului prin abstractizarea creării de fire.

Acest lucru se datorează faptului că puteți executa sarcinile într-un grup de fire la fel de ușor ca și utilizarea unui fir separat pentru fiecare sarcină. Acest lucru permite programului dvs. să urmărească și să gestioneze câte fire sunt folosite pentru sarcinile lucrătorilor.

Să presupunem că aveți 100 de lucrători care așteaptă să fie executați. Dacă începeți un fir pe un lucrător (după cum este prezentat mai sus), ați avea 100 de fire în cadrul programului dvs., ceea ce ar putea duce la blocaje în altă parte a programului. În schimb, dacă utilizați un pool de fire cu, spuneți 10 fire prealocate, cele 100 de sarcini vor fi executate de aceste fire unul după altul, astfel încât programul dvs. să nu fie afectat de resurse. În plus, aceste filete pot fi configurate astfel încât să stea în jur pentru a efectua sarcini suplimentare pentru dvs..

Un ExecutorService acceptă o runnable (explicată mai sus) și execută sarcina la un moment potrivit. a depune() , care acceptă sarcina Runnable, returnează o instanță a unei clase numite Viitor, care permite apelantului să urmărească starea sarcinii. În special, obține() permite apelantului să aștepte finalizarea sarcinii (și oferă codul de returnare, dacă este cazul).

În exemplul de mai jos, vom crea un ExecutorService utilizând metoda statică newSingleThreadExecutor (), care, după cum indică și numele, creează un fir unic pentru executarea sarcinilor. Dacă sunt depuse mai multe sarcini în timpul executării unei sarcini, ExecutorService cade aceste sarcini în execuție ulterioară.

Implementarea Runnable utilizată aici este aceeași cu cea descrisă mai sus.

ExecutorService esvc = Executors.newSingleThreadExecutor (); Lucrător care se poate executa = MyThread2 nou (sleepFor); Viitor viitor = esvc.submit (lucrător); System.out.printf ("[% s] thread principal \ n", Thread.currentThread (). ToString ()); future.get (); esvc.shutdown (); 

Rețineți că un ExecutorService trebuie să fie oprit corespunzător atunci când nu mai este necesar pentru depunerea altor sarcini.

Metoda 4: O callabilă folosită cu ExecutorService

Începând cu versiunea 1.5, Java a introdus o nouă interfață numită nevărsat. Este similar cu cea mai veche interfață Runnable cu diferența că metoda de execuție (numită apel() in loc de alerga()) poate returna o valoare. În plus, poate declara, de asemenea, că o Excepție pot fi aruncate.

Un ExecutorService poate accepta, de asemenea, sarcini implementate ca nevărsat și returnează a Viitor cu valoarea returnată de metoda la finalizare.

Iată un exemplu Mango clasa care extinde fruct clasa definită mai devreme și implementează nevărsat interfață. O sarcină costisitoare și consumatoare de timp este efectuată în cadrul apel() metodă.

clasa publică Mango extinde Fruit implements Callable public Integer apel () // calcul scump aici retur nou Integer (0);  

Iată codul pentru trimiterea unei instanțe a clasei la un ExecutorService. Codul de mai jos așteaptă și sarcina de a finaliza și tipări valoarea de retur.

ExecutorService esvc = Executors.newSingleThreadExecutor (); MyCallable worker = noul MyCallable (sleepFor); Viitorul viitor = esvc.submit (lucrător); System.out.printf ("[% s] thread principal \ n", Thread.currentThread (). ToString ()); System.out.println ("Activitatea returnată:" + future.get ()); esvc.shutdown (); 

Ce preferi?

În acest articol, am învățat câteva metode de scriere a codului multi-threaded în Java. Acestea includ:

  1. Extinderea Fir clasa este cea mai de bază și a fost disponibilă de la Java 1.0.
  2. Dacă aveți o clasă care trebuie să extindă o altă clasă într-o ierarhie de clasă, atunci puteți implementa runnable interfață.
  3. O facilitate mai modernă pentru crearea de fire este ExecutorService care poate accepta o instanță Runnable ca o sarcină pentru a rula. Avantajul acestei metode este că puteți utiliza un pool de fire pentru executarea sarcinilor. Un bazin de filete ajută la conservarea resurselor prin reutilizarea firelor.
  4. În sfârșit, puteți crea o sarcină prin implementarea nevărsat și trimiteți sarcina la un ExecutorService.

Care din aceste opțiuni credeți că veți folosi în următorul proiect? Spuneți-ne în comentariile de mai jos.

Explorați mai multe despre: Java.