Lesson 3
Simulating a Traffic Signal Controller with Deadlock Prevention
Simulating a Traffic Signal Controller with Deadlock Prevention

Welcome to our lesson on simulating a traffic signal controller with deadlock prevention. In our previous lessons, you learned about the essentials of concurrency and how to handle concurrent operations safely. In this lesson, we will explore how to apply these concepts to real-world scenarios where multiple entities need controlled access to shared resources, particularly focusing on deadlock prevention in concurrent systems.

What You’ll Learn

By the end of this lesson, you will have a strong understanding of:

  • How to simulate traffic control systems at intersections using Java.
  • Utilizing synchronization in Java to manage concurrent access to shared resources.
  • Implementing deadlock prevention techniques by controlling resource access with synchronized blocks.

This lesson will guide you through creating a simulation of vehicles at an intersection, where efficient handling and coordination prevent deadlocks, ensuring smooth flow across different directions.

The Traffic Control Problem

Imagine a busy intersection where vehicles from different directions (North-South and East-West) need to pass through safely. Without any control, two vehicles coming from opposite directions could enter the intersection at the same time, potentially resulting in a collision or a traffic jam. Additionally, if both directions attempt to cross the intersection at the same time, they could block each other, leading to a deadlock, where neither vehicle can proceed.

intersection

In the diagram above, you can see an intersection where vehicles travel in two directions: North-South and East-West. To control the traffic and prevent collisions or deadlocks, each direction has a corresponding lock, represented by the icons.

The locks ensure that vehicles from one direction must wait until the other direction has finished crossing, preventing potential conflicts at the intersection. By designing a traffic control system that utilizes these locks, we can manage traffic flow and prevent deadlocks, ensuring vehicles cross the intersection safely.

Simulating the Traffic Signal Controller

In this simulation, we will model an intersection where vehicles from different directions need to pass through without blocking one another. Let’s dive into the code and understand how each part works.

Intersection Class

To handle traffic safely and prevent deadlocks, we’ll define two separate locks for vehicles traveling North-South and East-West. Each vehicle must acquire the appropriate lock before it can cross the intersection, ensuring that no two vehicles from different directions cross at the same time.

Java
1public class Intersection { 2 private final Object northSouthLock = new Object(); 3 private final Object eastWestLock = new Object();

In the Intersection class, we declare two objects: northSouthLock and eastWestLock. These objects act as locks for controlling the flow of vehicles traveling in the North-South and East-West directions, respectively. The use of separate locks ensures that vehicles in different directions don't interfere with each other, allowing traffic to flow smoothly without conflict.

Java
1 public void crossNorthSouth(String vehicle) throws InterruptedException { 2 synchronized (northSouthLock) { 3 System.out.println(vehicle + " is crossing North-South"); 4 Thread.sleep(100); 5 System.out.println(vehicle + " has crossed North-South"); 6 } 7 }

The crossNorthSouth method is synchronized on northSouthLock, ensuring that only one vehicle can cross in the north-south direction at any given time. The Thread.sleep(100) simulates the time it takes for the vehicle to cross the intersection.

When the method is invoked, it acquires the northSouthLock, which guarantees that no other vehicle traveling North-South can enter the intersection until the current vehicle has crossed and released the lock. This is a critical step to ensure that only one vehicle crosses at a time in a specific direction, which helps prevent collisions.

Java
1 public void crossEastWest(String vehicle) throws InterruptedException { 2 synchronized (eastWestLock) { 3 System.out.println(vehicle + " is crossing East-West"); 4 Thread.sleep(100); 5 System.out.println(vehicle + " has crossed East-West"); 6 } 7 } 8}

Similarly, the crossEastWest method uses eastWestLock to manage vehicles crossing east-west. Both methods ensure that crossing happens safely and without conflict, helping prevent any potential deadlocks.

The logic in crossEastWest mirrors that of crossNorthSouth, but uses the eastWestLock. This approach isolates the two directions, so a vehicle crossing North-South does not interfere with one crossing East-West. The separate locks mean that there is no waiting between directions, helping to prevent the classic deadlock scenario where threads (or, in this case, vehicles) are waiting on each other to release a lock.

Running the Traffic Signal Controller

The Main class simulates the intersection by creating threads that represent vehicles traveling in different directions.

Java
1public class Main { 2 public static void main(String[] args) { 3 Intersection intersection = new Intersection();

We first create an Intersection object, which controls the flow of traffic through the intersection.

Java
1 // Create the northBound thread 2 Thread northBound = new Thread(() -> { 3 try { 4 while (true) { 5 intersection.crossNorthSouth("Car North"); 6 Thread.sleep(200); 7 } 8 } catch (InterruptedException e) { 9 Thread.currentThread().interrupt(); 10 } 11 });

The northBound thread simulates vehicles crossing from north to south. It continuously calls the crossNorthSouth method, simulating vehicles crossing one by one with a brief pause between crossings (Thread.sleep(200)).

Java
1 // Create the eastBound thread 2 Thread eastBound = new Thread(() -> { 3 try { 4 while (true) { 5 intersection.crossEastWest("Car East"); 6 Thread.sleep(200); 7 } 8 } catch (InterruptedException e) { 9 Thread.currentThread().interrupt(); 10 } 11 });

Similarly, the eastBound thread simulates vehicles crossing from east to west. Both threads run concurrently, representing real-time traffic flow through the intersection.

Java
1 // Start both threads 2 northBound.start(); 3 eastBound.start(); 4 } 5}

The start() method begins the traffic simulation. Both directions of traffic run in parallel, and vehicles continue to cross until the program is interrupted manually.

This simplified version runs indefinitely, continuously simulating cars crossing the intersection without any interruptions or manual stop.

Why It Matters

This topic is significant in the realm of concurrent programming and real-world applications like transportation systems. Understanding and implementing traffic control systems such as this one highlights the importance of:

  • Smooth Operation: Ensures that traffic flows smoothly without any blockages, a critical aspect of traffic management.
  • Deadlock Prevention: By controlling access to shared resources efficiently, we prevent deadlocks, ensuring system reliability.
  • Concurrency Skills: Enhances your ability to manage concurrent tasks, a valuable skill in the software development of complex systems.

Traffic control is one of many examples where smooth and efficient management of concurrent tasks is necessary. Mastering these skills will broaden your capability to design and implement systems that need to handle simultaneous operations without running into conflicts or resource blocks.

Now that you understand how to manage a traffic signal controller efficiently, you are equipped to handle more complex scenarios in concurrent programming. Let's move on to practicing these concepts!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.