Explorando Phaser: Como Coordenar Threads em Java com Flexibilidade

Por Gaspar Barancelli Junior em 13 de maio de 2024
Imagem ilustrativa sobre o post Explorando Phaser: Como Coordenar Threads em Java com Flexibilidade

Se você já se aventurou no mundo da programação concorrente em Java, provavelmente já se deparou com o desafio de coordenar a execução de várias threads de forma eficiente. Felizmente, a plataforma Java oferece uma variedade de ferramentas para lidar com isso, e uma delas é o Phaser. Neste artigo, vamos explorar o Phaser em Java, entender sua funcionalidade e como ele pode ser usado para coordenar threads de maneira eficaz.

O que é o Phaser?

O Phaser é uma classe fornecida pela API Java, localizada no pacote java.util.concurrent, que oferece recursos avançados para sincronizar a execução de threads. Ao contrário de outros mecanismos de sincronização, como CountDownLatch e CyclicBarrier, o Phaser permite uma coordenação mais flexível e dinâmica entre threads.

Funcionalidades do Phaser

O Phaser se destaca pela habilidade de segmentar processos em múltiplas fases, permitindo que as threads aguardem a conclusão de todas as outras em uma fase específica antes de avançar. Essa característica é particularmente útil em cenários que exigem sincronização em diferentes etapas de execução.

Exemplo

Sincronização em Fases

Neste primeiro exemplo vamos demonstra a utilização do Phaser para coordenar duas threads em um serviço executor, cada uma realizando tarefas em tempos distintos:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Phaser;

public class ExemploExecutorServiceComPhaser {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Phaser phaser = new Phaser(2); // O número 2 indica a quantidade de partes a serem registradas

        executorService.submit(() -> {
            System.out.println("Tarefa 1 iniciada");
            // Simulando uma tarefa demorada
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Tarefa 1 concluída");
            phaser.arrive(); // Avisa que esta parte chegou à barreira
        });

        executorService.submit(() -> {
            System.out.println("Tarefa 2 iniciada");
            // Simulando uma tarefa demorada
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Tarefa 2 concluída");
            phaser.arrive(); // Avisa que esta parte chegou à barreira
        });

        // Espera até que todas as partes tenham concluído
        phaser.awaitAdvance(phaser.getPhase());
        System.out.println("Tarefas concluídas");
        executorService.shutdown();
    }
}
Coordenação Dinâmica

Além disso, o Phaser oferece suporte para coordenação dinâmica de threads, o que significa que o número de partes envolvidas na sincronização pode mudar durante a execução do programa. Isso proporciona uma maior flexibilidade e adaptabilidade em cenários onde a estrutura do processo é dinâmica.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Phaser;

public class ExemploExecutorServiceComPhaser2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Phaser phaser = new Phaser();

        phaser.register(); // Registrando uma nova parte
        executorService.submit(() -> {
            System.out.println("Tarefa 1 iniciada");
            // Simulando uma tarefa demorada
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Tarefa 1 concluída");
            phaser.arriveAndDeregister(); // Avisa que esta parte chegou à barreira e não precisa mais participar
        });

        phaser.register(); // Registrando uma nova parte
        executorService.submit(() -> {
            System.out.println("Tarefa 2 iniciada");
            // Simulando uma tarefa demorada
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Tarefa 2 concluída");
            phaser.arriveAndDeregister(); // Avisa que esta parte chegou à barreira e não precisa mais participar
        });

        // Espera até que todas as partes tenham concluído
        phaser.awaitAdvance(phaser.getPhase());
        System.out.println("Tarefas concluídas");
        executorService.shutdown();
    }
}

Neste exemplo, as threads são registradas e deregistradas dinamicamente, permitindo uma adaptação flexível ao número de partes envolvidas na sincronização.

Conclusão

O Phaser em Java é uma ótima opção para coordenar a execução de threads de forma eficiente e flexível. Sua capacidade de sincronização em fases e suporte para coordenação dinâmica o tornam muito útil em cenários complexos de programação concorrente. Dominar o Phaser significa que você pode escrever código robusto e altamente concorrente em Java. Experimente usar o Phaser em seus projetos e aproveite os benefícios de uma melhor coordenação de threads!

// Compartilhe esse Post

💫
🔥 NOVO APP

Domine o Inglês em 30 dias!

Inteligência Artificial + Repetição Espaçada • Método cientificamente comprovado

✅ Grátis para começar 🚀 Resultados rápidos
×