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.