Traffic Signal Simulation Using CyclicBarrier

Traffic Signal Simulation with CyclicBarrier

Welcome back! In the last lesson, we learned how to manage resource allocation in a restaurant ordering system using semaphores. In this lesson, we’re going to apply similar synchronization principles using CyclicBarrier to simulate a traffic signal system. This lesson will deepen your understanding of task synchronization by coordinating the actions of multiple threads that depend on each other.

What You'll Learn

In this lesson, we’ll explore:

  • How to use CyclicBarrier to coordinate the behavior of multiple threads.
  • Implementing a traffic signal system at a four-way intersection.
  • How to manage thread execution with ExecutorService and cyclic synchronization.

By the end of this lesson, you’ll know how to apply CyclicBarrier in real-world scenarios like traffic management, where tasks need to synchronize before proceeding.

Recap: CyclicBarrier and Its Role in Synchronization

You’ve encountered various concurrency tools in previous lessons, such as semaphores for managing limited resources. CyclicBarrier is different—it’s a synchronization aid that makes multiple threads wait for each other before continuing their tasks. This is especially useful when actions must be synchronized, like cars waiting for a green light at an intersection.

In this lesson, we’ll use CyclicBarrier to simulate a traffic signal system. Cars from different directions (threads) arrive at the intersection and must wait for each other before they can proceed. The barrier makes sure that no car proceeds until all have arrived at the intersection, just like how traffic lights coordinate cars at a busy junction.

Setting Up the Traffic Signal System

We begin by defining the core structure for the traffic signal simulation. This includes setting up the CyclicBarrier and managing threads using ExecutorService.

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

public class TrafficSignalSimulation {

    private final CyclicBarrier barrier;
    private final ExecutorService executor;
    private final int totalCycles;

    public TrafficSignalSimulation(int numDirections, int totalCycles) {
        this.barrier = new CyclicBarrier(numDirections, this::changeTrafficLights);
        this.executor = Executors.newFixedThreadPool(numDirections);
        this.totalCycles = totalCycles;
    }
}

In this snippet:

  • CyclicBarrier: This is initialized with the number of directions (cars) needing synchronization. It waits for all cars to arrive at the intersection before allowing any of them to proceed.
  • ExecutorService: Manages the threads representing cars arriving from different directions. Each car (thread) moves independently, but is synchronized using the barrier.
  • totalCycles: This determines how many times the cars will go through the intersection before the simulation ends.
this.barrier = new CyclicBarrier(numDirections, this::changeTrafficLights);

Here, once all threads reach the barrier, the changeTrafficLights method is executed. This action simulates the event of traffic lights changing to allow cars to proceed through the intersection.

With this setup, we have the basic structure to simulate a traffic signal system where cars wait for each other at the intersection.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal