← Back to Multithreading Course

JAVA MULTITHREADING · CLASS 24

Coordinating Threads with CyclicBarrier

Sometimes, multiple threads need to reach the same point before any of them can continue. Java provides CyclicBarrier to help threads wait for one another.

1. Understanding the Problem

Imagine that three friends are going on a road trip.

Each friend is travelling separately, but they have agreed to meet at a particular location before continuing their journey together.

  • Friend 1 reaches the meeting point first.
  • Friend 2 reaches the meeting point next.
  • Friend 3 arrives last.

Should Friend 1 continue the journey alone? No. All three friends want to continue together.

So, the first two friends wait until the third friend arrives. Once everyone has reached the meeting point, they can continue.

This is similar to how CyclicBarrier works with threads.

2. What Is CyclicBarrier?

CyclicBarrier is a Java utility that allows a fixed number of threads to wait for one another at a common point.

We create a CyclicBarrier by specifying how many threads must reach it.

When a thread reaches the barrier, it waits. Once the required number of threads have arrived, they are all allowed to continue.

For example, if we create a CyclicBarrier for three threads:

  • Thread 1 reaches the barrier and waits.
  • Thread 2 reaches the barrier and waits.
  • Thread 3 reaches the barrier.
  • All three threads are now allowed to continue.

The important idea is that the threads wait for one another, rather than waiting for a separate countdown operation.

3. Creating a CyclicBarrier

CyclicBarrier belongs to the java.util.concurrent package.

Let's see how we create one:

import java.util.concurrent.CyclicBarrier;

CyclicBarrier barrier = new CyclicBarrier(3);

Here, the number 3 means that three threads must reach the barrier before they can continue.

A thread reaches the barrier by calling the await() method.

barrier.await();

When a thread calls await(), it waits until the other required threads also reach the same barrier.

4. Let's Write Our First Program

We will create three worker threads. Each worker will perform some preparation work.

After finishing its preparation, each worker will reach the barrier and wait for the other workers.

Once all three workers reach the barrier, they will continue with the next part of their work.

Here is the complete program:

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    public static void main(String[] args) {

        CyclicBarrier barrier = new CyclicBarrier(3);

        for (int i = 1; i <= 3; i++) {

            final int workerNumber = i;

            Thread worker = new Thread(() -> {

                try {

                    System.out.println(
                        "Worker " + workerNumber + " started preparation"
                    );

                    Thread.sleep(workerNumber * 1000);

                    System.out.println(
                        "Worker " + workerNumber + " reached the barrier"
                    );

                    barrier.await();

                    System.out.println(
                        "Worker " + workerNumber + " continues working"
                    );

                } catch (Exception e) {

                    System.out.println(
                        "Worker " + workerNumber + " was interrupted or "
                        + "could not cross the barrier"
                    );

                    Thread.currentThread().interrupt();
                }
            });

            worker.start();
        }
    }
}

5. Understanding the Program Step by Step

Step 1: Create the barrier

CyclicBarrier barrier = new CyclicBarrier(3);

We create a barrier that requires three threads to reach it.

Step 2: Create three worker threads

for (int i = 1; i <= 3; i++) {
    // Create and start three worker threads
}

Each worker performs its preparation independently.

Step 3: Simulate different preparation times

Thread.sleep(workerNumber * 1000);

We use sleep() to make the workers take different amounts of time.

  • Worker 1 sleeps for 1 second.
  • Worker 2 sleeps for 2 seconds.
  • Worker 3 sleeps for 3 seconds.

This helps us understand what happens when threads finish their preparation at different times.

Step 4: Reach the barrier

System.out.println("Worker " + workerNumber + " reached the barrier");

barrier.await();

After completing its preparation, each worker calls await().

The first worker waits. The second worker also waits. When the third worker arrives, the required number of threads have reached the barrier.

Step 5: Continue together

System.out.println("Worker " + workerNumber + " continues working");

Once all three workers have reached the barrier, they are released and can continue with the next statement.

Their messages may appear in any order because the threads continue executing independently.

6. Understanding the Execution Flow

Let's look at the sequence of events:

Worker 1 starts preparation
Worker 2 starts preparation
Worker 3 starts preparation

Worker 1 finishes preparation
Worker 1 reaches the barrier
Worker 1 waits...

Worker 2 finishes preparation
Worker 2 reaches the barrier
Worker 2 waits...

Worker 3 finishes preparation
Worker 3 reaches the barrier

All three threads are released

Worker 1 continues working
Worker 2 continues working
Worker 3 continues working

This is an illustration of the execution flow. The actual output order can vary because thread scheduling is not guaranteed.

The important thing is that no worker can pass the barrier until all three required threads have reached it.

7. Why Is It Called CyclicBarrier?

Here is something interesting about CyclicBarrier.

After the required threads reach the barrier and are released, the barrier becomes available for another round of coordination.

This means the same barrier can be used again by the same group of threads.

For example, imagine that three workers perform a task in multiple rounds:

  • Round 1: All workers complete their first task and wait.
  • Round 2: All workers complete their second task and wait.
  • Round 3: All workers complete their third task and wait.

The same CyclicBarrier can coordinate all these rounds. This reusable nature is why it is called CyclicBarrier.

8. CountDownLatch vs. CyclicBarrier

We have now learned two important coordination mechanisms. Let's understand their main differences.

CountDownLatch CyclicBarrier
Threads wait for a counter to reach zero. Threads wait for one another at a common point.
Uses countDown() to decrease the counter. Uses await() to reach the barrier and wait.
Cannot be reset after reaching zero. Can be reused for another round.
Useful when one or more threads wait for tasks to finish. Useful when a group of threads must meet before continuing.

Remember the simple difference:

  • CountDownLatch: Wait until a countdown finishes.
  • CyclicBarrier: Wait until the required group of threads reaches the same point.

9. Important Things to Remember

  • CyclicBarrier belongs to the java.util.concurrent package.
  • We specify how many threads must reach the barrier.
  • Each participating thread calls await() when it reaches the barrier.
  • Threads wait until the required number of threads arrive.
  • Once released, the barrier can be used again.
  • If a waiting thread is interrupted or the barrier fails, the barrier can become broken. Waiting threads may then receive a BrokenBarrierException.

For now, remember the main idea: CyclicBarrier helps a group of threads synchronize at a common point.

Test Your Understanding

Let's see how well you understood this class!

Question 1: What is the primary purpose of CyclicBarrier?

Question 2: Which method does a thread call to wait at a CyclicBarrier?

Question 3: What does new CyclicBarrier(3) mean?

Question 4: Why is it called CyclicBarrier?

Question 5: Which statement correctly describes CountDownLatch and CyclicBarrier?

Class Summary

Today, we learned how CyclicBarrier helps multiple threads coordinate their work.

  • CyclicBarrier makes a group of threads wait for one another.
  • We specify the required number of threads when creating it.
  • Each thread calls await() when it reaches the barrier.
  • All required threads are released when the barrier is reached.
  • The same barrier can be reused for another round.
  • CountDownLatch and CyclicBarrier solve related but different problems.

The key takeaway: Use CyclicBarrier when a group of threads needs to reach a common point before continuing together.

Coming Up Next: Class 25

Limiting Access with Semaphore

Imagine that a parking area has only three available spaces. Even if ten cars arrive, only three cars should be allowed to park at the same time.

In our next class, we will learn how Java's Semaphore helps limit the number of threads that can access a resource simultaneously.

0% Completed

You have completed 24 out of 46 classes.