Sincronização de Threads em Fases com Phaser em Java
A classe Phaser
, parte do pacote java.util.concurrent
, é um mecanismo de sincronização projetado para coordenar threads que precisam executar uma tarefa dividida em múltiplas fases. Pense nela como uma barreira de sincronização reutilizável e dinâmica: um grupo de threads chega à barreira, espera que todos os outros membros do grupo cheguem, e então todas são liberadas simultaneamente para iniciar a próxima fase.
Sua principal vantagem sobre outras barreiras, como CyclicBarrier
, é a capacidade de alterar dinamicamente o número de participantes registrados.
Os construtores mais comuns são:
Phaser()
: Cria umPhaser
sem participantes registrados inicialmente.Phaser(int parties)
: Cria umPhaser
já registrando um número inicial de participantes (parties
).
Os métodos essenciais para controlar o fluxo são:
int register()
: Registra um novo participante noPhaser
. O número de participantes necessários para avançar a fase aumenta em um.int arriveAndAwaitAdvance()
: O método mais comum. A thread que o invoca sinaliza que concluiu a fase atual e fica bloqueada até que todos os outros participantes registrados também cheguem a esta barreira.int arrive()
: Sinaliza a conclusão da fase, mas a thread não espera pelas outras e continua sua execução imediatamente.int arriveAndDeregister()
: Sinaliza a conclusão da fase final de um participante e o remove do registro. O número de participantes necessários para avançar as fases futuras diminui em um.int getPhase()
: Retorna o número da fase atual (a contagem começa em 0).
Exemplo Prático de Sincronização em Fases
Vamos criar um programa com três participantes: a thread main
e duas threads trabalhadoras (PhaseThread
). Juntas, elas executarão uma tarefa de 3 fases.
import java.util.concurrent.Phaser;
public class Program {
public static void main(String[] args) {
// Cria um Phaser com 1 participante inicial (a própria thread main)
Phaser phaser = new Phaser(1);
int currentPhase;
// Cria e inicia as threads trabalhadoras
new Thread(new WorkerThread(phaser, "Worker-1")).start();
new Thread(new WorkerThread(phaser, "Worker-2")).start();
// --- FASE 0 ---
currentPhase = phaser.getPhase();
System.out.println("Main: aguardando conclusão da fase " + currentPhase);
phaser.arriveAndAwaitAdvance(); // Main chega e espera os outros
System.out.println("--- Fase " + currentPhase + " concluída ---");
// --- FASE 1 ---
currentPhase = phaser.getPhase();
System.out.println("Main: aguardando conclusão da fase " + currentPhase);
phaser.arriveAndAwaitAdvance(); // Main chega e espera os outros
System.out.println("--- Fase " + currentPhase + " concluída ---");
// --- FASE 2 ---
currentPhase = phaser.getPhase();
System.out.println("Main: aguardando conclusão da fase " + currentPhase);
phaser.arriveAndAwaitAdvance(); // Main chega e espera os outros
System.out.println("--- Fase " + currentPhase + " concluída ---");
// Desregistra a thread main, pois seu trabalho acabou
phaser.arriveAndDeregister();
if (phaser.isTerminated()) {
System.out.println("O Phaser foi finalizado com sucesso.");
}
}
}
class WorkerThread implements Runnable {
private Phaser phaser;
private String name;
WorkerThread(Phaser p, String n) {
this.phaser = p;
this.name = n;
phaser.register(); // Registra esta nova thread como participante
}
public void run() {
// --- FASE 0 ---
System.out.println(name + " iniciando fase " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // Sinaliza conclusão da fase e espera
// --- FASE 1 ---
System.out.println(name + " iniciando fase " + phaser.getPhase());
phaser.arriveAndAwaitAdvance(); // Sinaliza conclusão da fase e espera
// --- FASE 2 ---
System.out.println(name + " iniciando fase " + phaser.getPhase());
phaser.arriveAndDeregister(); // Sinaliza conclusão final e se desregistra
}
}
Neste exemplo, o Phaser
é iniciado com 1 participante (a main thread
). Cada WorkerThread
se registra ao ser criada, elevando o total de participantes para 3.
Cada um dos três participantes, ao final de uma fase, invoca phaser.arriveAndAwaitAdvance()
. Este método bloqueia a thread até que o último dos 3 participantes também o invoque. Quando isso acontece, a barreira é liberada, o número da fase é incrementado, e todas as threads são desbloqueadas para iniciar o trabalho da próxima fase.
A saída do programa demonstra claramente a sincronização em cada etapa:
Main: aguardando conclusão da fase 0 Worker-1 iniciando fase 0 Worker-2 iniciando fase 0 --- Fase 0 concluída --- Main: aguardando conclusão da fase 1 Worker-2 iniciando fase 1 Worker-1 iniciando fase 1 --- Fase 1 concluída --- Main: aguardando conclusão da fase 2 Worker-1 iniciando fase 2 Worker-2 iniciando fase 2 --- Fase 2 concluída --- O Phaser foi finalizado com sucesso.
A ordem das mensagens "iniciando fase" dentro de uma mesma etapa pode variar, mas a mensagem "Fase X concluída" só é impressa após todos os participantes terem chegado à barreira daquela fase.
Resumo
- O
Phaser
sincroniza um grupo de threads através de múltiplas fases de uma tarefa. - Funciona como uma barreira de sincronização reutilizável cujo número de participantes pode mudar dinamicamente.
- O método
register()
adiciona um novo participante, enquantoarriveAndDeregister()
remove um. - O método
arriveAndAwaitAdvance()
é o ponto de encontro onde as threads esperam umas pelas outras antes de avançar para a próxima fase.