Clasa Phaser
Clasa Phaser permite sincronizarea thread-urilor care reprezintă faze sau etape distincte ale unei acțiuni comune. Phaser definește un obiect de sincronizare care așteaptă finalizarea unei anumite faze de către toate thread-urile participante. După aceea, Phaser trece la faza următoare și așteaptă din nou finalizarea acesteia.
Pentru a crea un obiect Phaser, putem folosi unul dintre următoarele constructoare:
Phaser()
Phaser(int parties)
Phaser(Phaser parent)
Phaser(Phaser parent, int parties)
Parametrul parties specifică numărul de participanți (în esență, thread-uri) care vor executa toate fazele acțiunii. Primul constructor creează un obiect Phaser fără participanți. Al doilea constructor înregistrează numărul de participanți transmis ca parametru. Al treilea și al patrulea constructor stabilesc și un obiect Phaser părinte.
Metodele principale ale clasei Phaser:
- int register(): înregistrează un participant care execută fazele și returnează numărul fazei curente (în mod normal, faza 0)
- int arrive(): informează că participantul a terminat faza și returnează numărul fazei curente
- int arriveAndAwaitAdvance(): similar metodei arrive(), dar așteaptă ca toți participanții să termine faza curentă înainte de a continua
- int arriveAndDeregister(): informează că participantul a terminat toate fazele și se deregistrează. Returnează numărul fazei curente sau un număr negativ dacă Phaser și-a încheiat activitatea
- int getPhase(): returnează numărul fazei curente
Exemplu de utilizare a clasei Phaser:
Mai jos este un exemplu de sincronizare a mai multor thread-uri care trec prin mai multe faze:
import java.util.concurrent.Phaser;
public class Program {
public static void main(String[] args) {
Phaser phaser = new Phaser(1); // Înregistrăm thread-ul principal
new Thread(new PhaseThread(phaser, "PhaseThread 1")).start();
new Thread(new PhaseThread(phaser, "PhaseThread 2")).start();
// Așteptăm finalizarea fazei 0
int phase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Faza " + phase + " s-a încheiat");
// Așteptăm finalizarea fazei 1
phase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Faza " + phase + " s-a încheiat");
// Așteptăm finalizarea fazei 2
phase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Faza " + phase + " s-a încheiat");
phaser.arriveAndDeregister(); // Deregistrăm thread-ul principal
}
}
class PhaseThread implements Runnable {
Phaser phaser;
String name;
PhaseThread(Phaser p, String n) {
this.phaser = p;
this.name = n;
phaser.register(); // Înregistrăm thread-ul
}
public void run() {
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // Finalizăm prima fază
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // Finalizăm a doua fază
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndDeregister(); // Finalizăm toate fazele și deregistrăm thread-ul
}
}
În acest exemplu, avem trei participanți care execută fazele: thread-ul principal și două thread-uri de tip PhaseThread. De aceea, atunci când se creează obiectul Phaser, îi este transmis numărul 1, care corespunde thread-ului principal. În constructorul clasei PhaseThread, se apelează metoda register() pentru a înregistra cele două thread-uri suplimentare.
Am fi putut omite apelul metodei register() și, în schimb, am fi putut inițializa Phaser cu 3 participanți direct, adică folosind Phaser phaser = new Phaser(3), deoarece avem trei participanți în total.
Fiecare fază pentru un participant reprezintă un set minimal de acțiuni. În cazul thread-urilor PhaseThread, acestea constau doar în afișarea unui mesaj, iar pentru thread-ul principal, acțiunea implică numărarea fazei curente folosind metoda getPhase(). Numerotarea fazelor începe de la zero.
Fiecare participant finalizează execuția unei faze apelând metoda phaser.arriveAndAwaitAdvance(). Această metodă blochează thread-ul până când toți participanții finalizează faza curentă.
Astfel, dacă un thread ajunge la finalul fazei și apelează această metodă, va trebui să aștepte ca și celelalte thread-uri să finalizeze înainte de a putea continua cu următoarea fază.
După ce toate fazele au fost finalizate, thread-urile se deregistrează folosind metoda arriveAndDeregister(). Aceasta semnalează că thread-ul și-a terminat participarea în toate fazele și nu mai trebuie sincronizat în continuare.
În final, rularea programului va oferi următorul rezultat:
PhaseThread 1 execută faza 0
PhaseThread 2 execută faza 0
PhaseThread 1 execută faza 1
PhaseThread 2 execută faza 1
Faza 0 a fost finalizată
Faza 1 a fost finalizată
PhaseThread 1 execută faza 2
PhaseThread 2 execută faza 2
Faza 2 a fost finalizată
În acest caz, se obține o ieșire puțin confuză. Astfel, mesajele despre execuția fazei 1 sunt afișate după mesajul despre încheierea fazei 0. Acest lucru este legat de multi-threading – fazele s-au încheiat, dar într-un fir de execuție încă nu a fost afișat mesajul despre finalizare, în timp ce alte fire au început deja execuția fazei următoare. Oricum, toate acestea se întâmplă după încheierea fazei.
Dar, pentru a fi mai clar, putem folosi sleep în firele de execuție:
public void run(){
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // raportăm că prima fază a fost atinsă
try{
Thread.sleep(200);
}
catch(InterruptedException ex){
System.out.println(ex.getMessage());
}
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // raportăm că a doua fază a fost atinsă
try{
Thread.sleep(200);
}
catch(InterruptedException ex){
System.out.println(ex.getMessage());
}
System.out.println(name + " execută faza " + phaser.getPhase());
phaser.arriveAndDeregister(); // raportăm încheierea fazelor și deregistrăm obiectele
}
Și în acest caz ieșirea va fi mai obișnuită, deși nu va afecta modul de lucru al fazelor.
PhaseThread 1 execută faza 0
PhaseThread 2 execută faza 0
Faza 0 a fost finalizată
PhaseThread 2 execută faza 1
PhaseThread 1 execută faza 1
Faza 1 a fost finalizată
PhaseThread 2 execută faza 2
PhaseThread 1 execută faza 2
Faza 2 a fost finalizată