Implementando Execução Alternada de Múltiplas Threads com Condition em Java

Em programação concorrente, a classe Condition do pacote java.util.concurrent.locks é essencial para coordenar a execução de threads. Vamos explorar dois cenários comuns onde threads precisam executar em uma ordem específica: impressão sequencial e impressão com frequências variadas.

Cenário 1: Impressão Sequencial de A, B, C

Neste cenário, três threads — A, B e C — devem imprimir seus nomes em ordem, repetindo o ciclo 10 vezes. Cada thread executa sua tarefa e, em seguida, snializa a próxima thread a prosseguir.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AlternatingPrint {
    public static void main(String[] args) {
        SequencePrinter printer = new SequencePrinter();

        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printFirst(i);
            }
        }, "A");

        Thread threadB = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printSecond(i);
            }
        }, "B");

        Thread threadC = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printThird(i);
            }
        }, "C");

        threadA.start();
        threadB.start();
        threadC.start();
    }
}

class SequencePrinter {
    private final Lock lock = new ReentrantLock();
    private final Condition conditionFirst = lock.newCondition();
    private final Condition conditionSecond = lock.newCondition();
    private final Condition conditionThird = lock.newCondition();
    private int currentTurn = 1; // 1 para primeira thread, 2 para segunda, 3 para terceira

    public void printFirst(int cycle) {
        lock.lock();
        try {
            while (currentTurn != 1) {
                conditionFirst.await();
            }
            System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            currentTurn = 2;
            conditionSecond.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void printSecond(int cycle) {
        lock.lock();
        try {
            while (currentTurn != 2) {
                conditionSecond.await();
            }
            System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            currentTurn = 3;
            conditionThird.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void printThird(int cycle) {
        lock.lock();
        try {
            while (currentTurn != 3) {
                conditionThird.await();
            }
            System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            currentTurn = 1;
            conditionFirst.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
}

Cenário 2: Impressão com Repetições Variadas (A, B B B, C C C C C)

Aqui, as threads imprimem com diferentes frequências: A uma vez, B três vezes, e C cinco vezes, em um ciclo de 10 iterações. A sincronização garante que a sequência se mantenha consistente.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class VariablePrint {
    public static void main(String[] args) {
        FrequencyPrinter printer = new FrequencyPrinter();

        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printFirst(i);
            }
        }, "A");

        Thread threadB = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printSecond(i);
            }
        }, "B");

        Thread threadC = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                printer.printThird(i);
            }
        }, "C");

        threadA.start();
        threadB.start();
        threadC.start();
    }
}

class FrequencyPrinter {
    private final Lock syncLock = new ReentrantLock();
    private final Condition condFirst = syncLock.newCondition();
    private final Condition condSecond = syncLock.newCondition();
    private final Condition condThird = syncLock.newCondition();
    private int activeThread = 1; // Indica qual thread está ativa

    public void printFirst(int cycle) {
        syncLock.lock();
        try {
            while (activeThread != 1) {
                condFirst.await();
            }
            // A imprime uma vez
            System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            activeThread = 2;
            condSecond.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            syncLock.unlock();
        }
    }

    public void printSecond(int cycle) {
        syncLock.lock();
        try {
            while (activeThread != 2) {
                condSecond.await();
            }
            // B imprime três vezes
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            }
            activeThread = 3;
            condThird.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            syncLock.unlock();
        }
    }

    public void printThird(int cycle) {
        syncLock.lock();
        try {
            while (activeThread != 3) {
                condThird.await();
            }
            // C imprime cinco vezes
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " - Ciclo " + cycle);
            }
            activeThread = 1;
            condFirst.signal();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            syncLock.unlock();
        }
    }
}

O uso de Condition com ReentrantLock oferece controle preciso sobre a ordem de execução das threads, essencial para cenários de sincronização complexos em ambientes concorrentes.

Tags: java Multithreading Condition ReentrantLock sincronização de threads

Publicado em 6-9 02:47 por Thomas