Aqui estão exemplos de código para os sincronizadores mencionados no item 80, com explicações de uso para facilitar o estudo:
1. CountDownLatch: Barreira de uso único para coordenação de threads
O CountDownLatch permite que uma ou mais threads aguardem até que um conjunto de operações realizadas por outras threads seja concluído.
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfWorkers = 3;
CountDownLatch latch = new CountDownLatch(numberOfWorkers);
for (int i = 0; i < numberOfWorkers; i++) {
new Thread(new Worker(latch, "Worker-" + i)).start();
}
System.out.println("Waiting for workers to finish...");
latch.await(); // Aguarda todos os trabalhadores chamarem latch.countDown()
System.out.println("All workers are done. Proceeding...");
}
static class Worker implements Runnable {
private final CountDownLatch latch;
private final String name;
Worker(CountDownLatch latch, String name) {
this.latch = latch;
this.name = name;
}
@Override
public void run() {
System.out.println(name + " is working...");
try {
Thread.sleep((long) (Math.random() * 2000)); // Simula trabalho
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(name + " finished.");
latch.countDown(); // Decrementa o contador
}
}
}
2. Semaphore: Controle de acesso a recursos compartilhados
O Semaphore gerencia um conjunto de permissões para controlar o acesso a recursos limitados.
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
int permits = 2; // Número de permissões disponíveis
Semaphore semaphore = new Semaphore(permits);
for (int i = 1; i <= 5; i++) {
new Thread(new Task(semaphore, "Task-" + i)).start();
}
}
static class Task implements Runnable {
private final Semaphore semaphore;
private final String name;
Task(Semaphore semaphore, String name) {
this.semaphore = semaphore;
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " is waiting for a permit...");
semaphore.acquire(); // Adquire uma permissão
System.out.println(name + " got a permit and is working...");
Thread.sleep((long) (Math.random() * 2000)); // Simula trabalho
System.out.println(name + " is releasing a permit.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // Libera a permissão
}
}
}
}
3. CyclicBarrier: Sincronização em pontos de barreira reutilizáveis
O CyclicBarrier sincroniza várias threads em um ponto comum (barreira). Ele pode ser reutilizado após todas as threads atingirem o ponto de barreira.
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int numberOfThreads = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, () -> {
System.out.println("All threads have reached the barrier. Proceeding...");
});
for (int i = 0; i < numberOfThreads; i++) {
new Thread(new Task(barrier, "Thread-" + i)).start();
}
}
static class Task implements Runnable {
private final CyclicBarrier barrier;
private final String name;
Task(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " is performing some work...");
Thread.sleep((long) (Math.random() * 2000)); // Simula trabalho
System.out.println(name + " reached the barrier.");
barrier.await(); // Aguarda todas as threads chegarem à barreira
System.out.println(name + " passed the barrier.");
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}
}
4. Phaser: Sincronização avançada e dinâmica de threads
O Phaser é semelhante ao CyclicBarrier, mas suporta threads que entram e saem dinamicamente.
import java.util.concurrent.Phaser;
public class PhaserExample {
public static void main(String[] args) {
Phaser phaser = new Phaser(1); // Registra o "partida principal"
for (int i = 0; i < 3; i++) {
new Thread(new Task(phaser, "Task-" + i)).start();
}
// Avança para a próxima fase após garantir que todas as threads registradas concluíram
System.out.println("Main thread waiting for phase 1 completion...");
phaser.arriveAndAwaitAdvance();
System.out.println("All tasks completed phase 1. Main thread moving to phase 2...");
phaser.arriveAndDeregister(); // Desregistra a thread principal
}
static class Task implements Runnable {
private final Phaser phaser;
private final String name;
Task(Phaser phaser, String name) {
this.phaser = phaser;
this.name = name;
phaser.register(); // Registra a thread no Phaser
}
@Override
public void run() {
System.out.println(name + " is working on phase 1...");
try {
Thread.sleep((long) (Math.random() * 2000)); // Simula trabalho
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(name + " completed phase 1.");
phaser.arriveAndAwaitAdvance(); // Indica chegada na fase atual e aguarda
System.out.println(name + " is working on phase 2...");
try {
Thread.sleep((long) (Math.random() * 2000)); // Simula trabalho
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(name + " completed phase 2.");
phaser.arriveAndDeregister(); // Indica chegada e desregistra
}
}
}
Esses exemplos ajudam a entender o funcionamento de cada sincronizador. Você pode experimentar ajustando os números de threads e tempos para observar os efeitos no comportamento de sincronização.
Top comments (0)