Differences Between Phaser, CyclicBarrier, and CountDownLatch in Java ?

FeaturePhaserCyclicBarrierCountDownLatch
PurposeSynchronize multiple threads across multiple phases.Synchronize a fixed number of threads at a single point.Wait for a set number of events or threads to complete.
Dynamic ThreadingAllows dynamic registration/deregistration of threads.Number of threads is fixed at initialization.Count is fixed at initialization.
PhasesSupports multiple phases of synchronization.Single synchronization point (can be reused).Single-use latch (not reusable).
Reset/ReuseYes, resets automatically for new phases.Yes, can be reused after the barrier is tripped.No, cannot be reused once the count reaches zero.
API ComplexityFlexible and feature-rich.Simple and straightforward.Simple and straightforward.
Thread TerminationCan terminate after reaching a specific condition.Does not support termination.No termination support, only counts down to zero.
Best Use CaseMulti-phase tasks with dynamic participation.Fixed-size thread pools or teams synchronizing at one point.Tasks where one thread waits for others to complete.

Examples

1. Phaser

Use case: Synchronizing threads in multiple phases with dynamic registration and deregistration.

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1); // Main thread registers itself

        for (int i = 1; i <= 3; i++) {
            phaser.register();
            final int threadId = i;
            new Thread(() -> {
                System.out.println("Thread " + threadId + " - Phase 1 started");
                phaser.arriveAndAwaitAdvance();
                System.out.println("Thread " + threadId + " - Phase 1 completed");

                System.out.println("Thread " + threadId + " - Phase 2 started");
                phaser.arriveAndAwaitAdvance();
                System.out.println("Thread " + threadId + " - Phase 2 completed");

                phaser.arriveAndDeregister();
            }).start();
        }

        phaser.arriveAndAwaitAdvance(); // Main thread syncs Phase 1
        System.out.println("Main thread - Phase 1 completed");
        phaser.arriveAndAwaitAdvance(); // Main thread syncs Phase 2
        System.out.println("Main thread - Phase 2 completed");

        phaser.arriveAndDeregister(); // Deregister main thread
    }
}

Output:

Thread 1 - Phase 1 started
Thread 2 - Phase 1 started
Thread 3 - Phase 1 started
Main thread - Phase 1 completed
Thread 1 - Phase 1 completed
Thread 2 - Phase 1 completed
Thread 3 - Phase 1 completed
Thread 1 - Phase 2 started
Thread 2 - Phase 2 started
Thread 3 - Phase 2 started
Main thread - Phase 2 completed
Thread 1 - Phase 2 completed
Thread 2 - Phase 2 completed
Thread 3 - Phase 2 completed

2. CyclicBarrier

Use case: Fixed number of threads synchronizing at a single barrier point.

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("All threads have reached the barrier, proceeding to the next step.");
        });

        for (int i = 1; i <= 3; i++) {
            final int threadId = i;
            new Thread(() -> {
                System.out.println("Thread " + threadId + " is performing work.");
                try {
                    Thread.sleep(1000); // Simulate work
                    System.out.println("Thread " + threadId + " is waiting at the barrier.");
                    barrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("Thread " + threadId + " has crossed the barrier.");
            }).start();
        }
    }
}

Output:

Thread 1 is performing work.
Thread 2 is performing work.
Thread 3 is performing work.
Thread 1 is waiting at the barrier.
Thread 2 is waiting at the barrier.
Thread 3 is waiting at the barrier.
All threads have reached the barrier, proceeding to the next step.
Thread 1 has crossed the barrier.
Thread 2 has crossed the barrier.
Thread 3 has crossed the barrier.

3. CountDownLatch

Use case: One thread waits for multiple threads or events to complete.

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);

        for (int i = 1; i <= 3; i++) {
            final int threadId = i;
            new Thread(() -> {
                System.out.println("Thread " + threadId + " is performing work.");
                try {
                    Thread.sleep(1000); // Simulate work
                    System.out.println("Thread " + threadId + " has completed work.");
                    latch.countDown(); // Decrement the latch count
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        try {
            System.out.println("Main thread is waiting for other threads to finish.");
            latch.await(); // Wait for the latch count to reach zero
            System.out.println("All threads have finished, main thread continues.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output:

Thread 1 is performing work.
Thread 2 is performing work.
Thread 3 is performing work.
Thread 1 has completed work.
Thread 2 has completed work.
Thread 3 has completed work.
Main thread is waiting for other threads to finish.
All threads have finished, main thread continues.

Summary

  • Use Phaser for multi-phase tasks with dynamic participation.
  • Use CyclicBarrier for fixed-thread synchronization at a single barrier point, with optional reuse.
  • Use CountDownLatch for tasks where one thread waits for others to complete and the count is fixed.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *