Contrasting CountDownLatch and CyclicBarrier in Java Concurrency

The fundamental distinction between CountDownLatch and CyclicBarrier lies in which thread gets blocked. When using CountDownLatch, the await() method is typically invoked by the main or coordinating thread, causing it to block until worker threads signal completion via countDown(). In contrast, CyclicBarrier has the worker threads themselves call await(), blocking them until all participating threads reach the synchronization point, leaving the main thread free to continue execution.

Another structural difference is how the counter is decremented. With CyclicBarrier, the internal count decreases automatically when a thread invokes await(). CountDownLatch requires an explicit call to countDown() from the worker thread. While CyclicBarrier can be reused after parties are released and CountDownLatch is a one-time event, reusability is a secondary characteristic rather than the core behavioral difference.

CyclicBarrier Implementation

In the following example, the main thread submits two batches of tasks consecutively. Because the barrier blocks the worker threads, the main thread is not delayed and can dispatch all tasks immediately.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierDemo {
    private static final int PARTIES = 3;
    private static final CyclicBarrier BARRIER = new CyclicBarrier(PARTIES);

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(PARTIES * 2);

        // First batch
        for (int i = 0; i < PARTIES; i++) {
            executor.submit(new WorkerTask("Batch1-Worker-" + i));
        }

        // Second batch starts immediately since main thread is not blocked
        for (int i = 0; i < PARTIES; i++) {
            executor.submit(new WorkerTask("Batch2-Worker-" + i));
        }

        executor.shutdown();
    }

    static class WorkerTask implements Runnable {
        private final String name;

        WorkerTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(name + " is processing data...");
                Thread.sleep(1000); // Simulate work
                System.out.println(name + " reached the barrier.");
                BARRIER.await();
                System.out.println(name + " proceeds after barrier.");
            } catch (InterruptedException | BrokenBarrierException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

CountDownLatch Implementation

Here, the main thread waits for each batch to finish before proceeding to schedule the next one. The await() call halts the coordinating thread, creating a distinct separation between the two phases of execution.

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

public class CountDownLatchDemo {
    private static final int TASKS = 3;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(TASKS);

        // First batch
        CountDownLatch latch1 = new CountDownLatch(TASKS);
        for (int i = 0; i < TASKS; i++) {
            executor.submit(new LatchTask("Batch1-Task-" + i, latch1));
        }
        latch1.await(); // Main thread blocks here
        System.out.println("First batch completed. Main thread proceeds.");

        // Second batch
        CountDownLatch latch2 = new CountDownLatch(TASKS);
        for (int i = 0; i < TASKS; i++) {
            executor.submit(new LatchTask("Batch2-Task-" + i, latch2));
        }
        latch2.await(); // Main thread blocks here again
        System.out.println("Second batch completed. Main thread proceeds.");

        executor.shutdown();
    }

    static class LatchTask implements Runnable {
        private final String name;
        private final CountDownLatch latch;

        LatchTask(String name, CountDownLatch latch) {
            this.name = name;
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                System.out.println(name + " is processing data...");
                Thread.sleep(1000); // Simulate work
                System.out.println(name + " finished processing.");
                latch.countDown();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Tags: java Concurrency CountDownLatch CyclicBarrier multithreading

Posted on Thu, 18 Jun 2026 17:35:41 +0000 by Pinkmischief