A multithreaded traffic simulation built in Java that demonstrates concurrency concepts through vehicles navigating a grid of intersections. Built for CPSC 222 (Concurrency) at the University of Northern British Columbia.
Authors: Joshua Payne, Grayson Riemer, Evan Johnson -- UNBC
The simulation models traffic flow across a grid of intersections. Every vehicle and every intersection controller runs on its own thread. Vehicles spawn at the edges of the map, travel through the road network, and despawn when they reach an exit.
The project is a demonstration of how shared resources, and mutual exclusion play out in practice. With enough vehicles on the road, the simulation naturally produces gridlock, vehicles end up in circular wait chains where each one holds a road node that the next vehicle needs, and none of them can move. This is deadlock, and you can watch it happen in real time.
Vehicles as threads. Each vehicle is a Runnable that repeatedly attempts to move forward into the next node on its path. A node can only be occupied by one vehicle at a time. If the next node is taken, the vehicle waits and retries.
Intersection controllers as threads. Each intersection runs its own thread that cycles through six signal phases on a timer (straight traffic, left turns, cross traffic, etc.), similar to real traffic lights. Vehicles can only enter an intersection node if the current signal phase permits movement from their direction.
Maps from text files. The road network is defined in simple text files where characters represent road segments (U, D, L, R), intersections (4), spawners (*), and despawners (#). Five maps are included, from a single intersection up to larger grids.
Deadlock emergence. On denser maps like the 2x2 grid shown above, vehicles from all four directions compete for intersection nodes simultaneously. When enough vehicles pile up, they form circular dependencies, classic deadlock conditions that arise naturally from the concurrent design rather than being artificially constructed.
Below is a snapshot of gridlock forming on the 2x2 map. Vehicles queued in a turning lane have nowhere to go because the nodes ahead are already occupied, and the vehicles occupying those nodes are themselves blocked. Nobody can move.
The map system scales from a single intersection up to large grids.
| 4x4 grid (map_03) | 8x8 grid (map_04) |
|---|---|
![]() |
![]() |
Maps are NxN text grids read from resources/maps/. Characters:
| Char | Meaning |
|---|---|
U D L R |
Road node with traffic flowing up, down, left, or right |
4 |
Intersection (2x2 block) |
* |
Spawner (vehicle entry point, placed on map edges) |
# |
Despawner (vehicle exit point, placed on map edges) |
0 |
Empty / grass |
Requires Java 17+.
javac -d out src/**/*.java
java -cp out Main
Click "Start" to begin spawning vehicles.
src/
Main.java -- Window setup and thread initialization
controller/
AgentManager.java -- Spawns and manages vehicle threads
IntersectionController.java -- Signal phase cycling (runs per intersection)
data/
Agent.java / Vehicle.java -- Vehicle thread logic and movement
Node.java / IntersectionNode.java -- Road nodes with occupancy control
SpawnerNode.java / DespawnerNode.java -- Map entry/exit points
SimulationMap.java -- Map data structure
util/
MapReader.java -- Parses map text files into node graphs
view/
SimulationPanel.java -- Swing rendering with tile and car sprites
resources/
maps/ -- Map definitions (map_01 through map_05)
textures/ -- Road tiles, car sprites, signal overlays



