Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 5 of 5

Thread: Help with traffic light program and threads

  1. #1
    Junior Member
    Join Date
    Sep 2021
    Posts
    11
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Help with traffic light program and threads

    I need help starting a new thread after I stop the current thread. I keep getting IllegalThreadStateException and I dont know how to fix it.

    Exception happens at TrafficMitigation.lambda$0(TrafficMitigation.java: 270)

    TrafficMitigation.java
    /**
    * 
    * TrafficMitigation is a GUI application that runs multiple threads concurrently
    * to display current time stamps in 1 second intervals and a real-time Traffic 
    * light display for three major intersections using multiple JFrame components
    * and listeners
    */
     
    //=========
    //packages
    //=========
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import java.util.concurrent.atomic.*;
     
    //=================================
    //start of class TrafficMitigation
    //=================================
    public class TrafficMitigation extends JFrame implements Runnable, ChangeListener {
    	private static final long serialVersionUID = 1L;
     
    	//==================
    	//JFrame components
    	//==================
    	public static JLabel currentTime = new JLabel();
     
    	public static JLabel trafficLight1 = new JLabel();
    	private static JLabel trafficLightLabel1;
     
    	public static JLabel trafficLight2 = new JLabel();
    	private static JLabel trafficLightLabel2;
     
    	public static JLabel trafficLight3 = new JLabel();
    	private static JLabel trafficLightLabel3;
     
    	private static JButton startButton;
    	private static JButton pauseButton;
    	private static JButton stopButton;
     
    	private static JComboBox<Integer> carsJComboBox;
    	private static JComboBox<Integer> intersectionJComboBox;
     
    	private static JLabel carBoxLabel;
    	private static JLabel intersectionBoxLabel;
    	private static JLabel header1;
    	private static JLabel header2;
    	private static JLabel timeLabel;	
     
    	private static JSlider car1Slider;
    	private static JSlider car2Slider;
    	private static JSlider car3Slider;
     
    	//==========
    	//variables
    	//==========
    	private static boolean running;
    	private static Thread thread;
     
    	//=========
    	//invoking
    	//=========
    	private static final AtomicBoolean simIsRunning = new AtomicBoolean(false);
     
    	//=============================================
    	//invoking and each object gets its own thread
    	//=============================================
    	private Intersection intersection1 = new Intersection("Intersection 1 Thread", trafficLight1);
    	private Intersection intersection2 = new Intersection("Intersection 2 Thread", trafficLight2);
    	private Intersection intersection3 = new Intersection("Intersection 3 Thread", trafficLight3);
     
    	private Car car1 = new Car("Car 1 Thread", 1, 0);
    	private Car car2 = new Car("Car 2 Thread", 300, 0);
    	private Car car3 = new Car("Car 3 Thread", 700, 0);
     
    	//=======
    	//arrays
    	//=======
    	private Car[] carArray = {car1, car2, car3};
    	private Intersection[] intersectionArray = {intersection1, intersection2, intersection3};
    	private String[] tableColumnNames = {"Car", "X-Pos", "Y-Pos", "Speed km/h"};
     
    	//=========
    	//2D array
    	//=========
    	private Object[][] trafficData = {
    			{"Car 1", car1.getPosition(), 0, 0},
    			{"Car 2", car2.getPosition(), 0, 0},
    			{"Car 3", car3.getPosition(), 0, 0}
    	};
     
    	//============================
    	//JTable for all traffic data
    	//============================
    	private JTable dataTable = new JTable(trafficData, tableColumnNames);
     
    	//============================================
    	//displays GUI window to the user and passing
    	//it to class Thread. Also Giving the time
    	//its own thread
    	//============================================
    	public static void main(String args[]) {
            TrafficMitigation gui = new TrafficMitigation();        
     
            gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	    gui.setLocation(440, 100);
    	    gui.setResizable(false);
    	    gui.setSize(600, 700);
    	    gui.setVisible(true);
     
    	    thread = new Thread(gui);
     
    	    Thread time = new Thread(new Time());
    		time.start();
    	}
     
    	//==================================
    	//initializes JFrame components and
    	//adds them to the GUI window
    	//==================================
    	public TrafficMitigation() {
    		super("Traffic Congestion Mitigation Company");
     
    		setLayout(new GridLayout(9, 1));
     
    		trafficLightLabel1 = new JLabel("Traffic Light 1 (1000)");
    		trafficLightLabel2 = new JLabel("Traffic Light 2 (2000)");
    		trafficLightLabel3 = new JLabel("Traffic Light 3 (3000)");		
     
    		startButton = new JButton("Start"); 
    		pauseButton = new JButton("Pause");
    		stopButton = new JButton("Stop");
     
    		carsJComboBox = new JComboBox<>();
    		carsJComboBox.addItem(1);
    		carsJComboBox.addItem(2);
    		carsJComboBox.addItem(3);
    		carsJComboBox.addItem(4);
    		carsJComboBox.addItem(5);
     
    		intersectionJComboBox = new JComboBox<>();
    		intersectionJComboBox.addItem(1);
    		intersectionJComboBox.addItem(2);
    		intersectionJComboBox.addItem(3);
    		intersectionJComboBox.addItem(4);
    		intersectionJComboBox.addItem(5);
     
    		carBoxLabel = new JLabel("Number of Cars");
    		intersectionBoxLabel = new JLabel("Number of Intersections");
     
    		header1 = new JLabel("Welcome to the Traffic Congestion Mitigation Simulator", SwingConstants.CENTER);
    		header2 = new JLabel("\nClick the Start button to begin", SwingConstants.CENTER);
     
    		timeLabel = new JLabel("Current Time: ");		
     
    		car1Slider = new JSlider(0, 3000);
    		car1Slider.setMajorTickSpacing(1000);
    		car1Slider.setPaintTicks(true);
    		car1Slider.setPaintLabels(true);
     
    		car2Slider = new JSlider(0, 3000);
    		car2Slider.setMajorTickSpacing(1000);
    		car2Slider.setPaintTicks(true);
    		car2Slider.setPaintLabels(true);
     
    		car3Slider = new JSlider(0, 3000);
    		car3Slider.setMajorTickSpacing(1000);
    		car3Slider.setPaintTicks(true);
    		car3Slider.setPaintLabels(true);		
     
    		//=====================================
    		//JPanel's for headers, buttons, time,
    		//intersections, datatable
    		//=====================================
    		JPanel headerPanel = new JPanel();
    		headerPanel.setLayout(new GridLayout(2,1));
    		headerPanel.add(header1);
    		headerPanel.add(header2);
     
    		JPanel buttonPanel = new JPanel();
    		buttonPanel.add(startButton);
    		buttonPanel.add(pauseButton);
    		buttonPanel.add(stopButton);
     
    		JPanel timePanel = new JPanel();
    		timePanel.add(timeLabel);
    		timePanel.add(currentTime);
     
    		JPanel boxPanel = new JPanel();
    		boxPanel.add(carBoxLabel);
    		boxPanel.add(carsJComboBox);
    		boxPanel.add(intersectionBoxLabel);
    		boxPanel.add(intersectionJComboBox);
     
    		JPanel lightPanel = new JPanel();
       		lightPanel.add(trafficLightLabel1);
    		lightPanel.add(trafficLight1);
    		lightPanel.add(trafficLightLabel2);
    		lightPanel.add(trafficLight2);
    		lightPanel.add(trafficLightLabel3);
    		lightPanel.add(trafficLight3);
     
    		JScrollPane scrollPane = new JScrollPane(dataTable);
    		JPanel dataPanel = new JPanel();
    		dataPanel.add(scrollPane);		
     
    		//================================
    		//add JPanels and JSliders to GUI
    		//================================
    		add(headerPanel);
    		add(timePanel);
    		add(boxPanel);
    		add(buttonPanel);
    		add(lightPanel);
    		add(car1Slider);
    		add(car2Slider);
    		add(car3Slider);
    		add(dataPanel);
     
    		//==============================================
    		//add ChangeListners and set Values to JSliders
    		//==============================================
    		car1Slider.addChangeListener(this);
    		car1Slider.setValue(car1.getPosition());
    		car2Slider.addChangeListener(this);
    		car2Slider.setValue(car2.getPosition());
    		car3Slider.addChangeListener(this);				
    		car3Slider.setValue(car3.getPosition());
    		setButtons();
     
    		//=========================
    		//setting JTable Viewports
    		//=========================
    		dataTable.setPreferredScrollableViewportSize(new Dimension(400, 300));
    		dataTable.setFillsViewportHeight(true);
     
    		//===================
    		//run current thread
    		//===================
    		running = Thread.currentThread().isAlive();		
    	}
     
    	//==================================
    	//handles all JButtons ActionEvents
    	//==================================
    	private void setButtons() {
     
    		//====================================
    		//handles "Start" buttton ActionEvent
    		//====================================
    		startButton.addActionListener((ActionEvent e) -> {
     
    			//=================================================================
    			//begin threads for cars and intersections when "Start" is clicked
    			//=================================================================
    			if(!simIsRunning.get()) {
    				System.out.println(Thread.currentThread().getName() + " calling start");
     
    				try {
    					intersection1.start();
    					intersection2.start();
    					intersection3.start();
    					car1.start();
    					car2.start();
    					car3.start();
    					thread.start();  /**Exception thrown here
                                                                   */
    				}
    				catch(IllegalThreadStateException itse) {
    					itse.printStackTrace();;
    				}
    				catch(NullPointerException npe) {
    					npe.printStackTrace();
    				}				
    			}
    			simIsRunning.set(true);
    		});
     
    		//===================================
    		//handles "Pause" button ActionEvent
    		//===================================
    		pauseButton.addActionListener((ActionEvent e) -> {
     
    			if(simIsRunning.get()) {
     
    				//=====================================================
    				//loop through cars and intersections to call paused()
    				//=====================================================
    				for(Car i: carArray) {
    					i.paused();
    					System.out.println(Thread.currentThread().getName() + " calling pause");
    				}
     
    				//=======================================
    				//for loop to interrupt sleeping threads
    				//=======================================
    				for(Intersection i: intersectionArray) {
    					i.interrupt();
    					i.paused();
    				}
     
    				//=========================================
    				//-changes pause button text to "Continue"
    				//-sets simluation running to false
    				//=========================================
    				pauseButton.setText("Continue");
    				simIsRunning.set(false);
    			} 
     
    			//=============================
    			//"Continue" button is clicked
    			//=============================
    			else {
     
    				//====================================
    				//for loop to resume threads for cars
    				//====================================
    				for(Car i:carArray) {
     
    					if(i.paused.get()) {
    						i.resume();
    						System.out.println(Thread.currentThread().getName() + " calling resume");
    					}
    				}
     
    				//=============================================
    				//for loop to resume threads for intersections
    				//=============================================
    				for(Intersection i: intersectionArray) {
    					i.resume();
    				}
     
    				//=========================================
    				//-changes continue button text to "Pause"
    				//-sets simluation running to true
    				//=========================================
    				pauseButton.setText("Pause");
    				simIsRunning.set(true);
    			}
    		});
     
    		//==================================
    		//handles "Stop" button ActionEvent
    		//==================================
    		stopButton.addActionListener((ActionEvent e) -> {
     
    			if(simIsRunning.get()) {
    				System.out.println(Thread.currentThread().getName() + " calling stop");
     
    				//=================================
    				//for loop to stop all car threads
    				//=================================
    				for(Car i: carArray) {
    					i.stop();
    				}
     
    				//==========================================
    				//for loop to stop all intersection threads
    				//==========================================
    				for(Intersection i: intersectionArray) {
    					i.stop();
    				}
    				simIsRunning.set(false);
    			}
    		});
    	}
     
    	//=========================================
    	//checks status of the intersection. Stops
    	//car threads for running the lights
    	//=========================================
    	private void getData() {
    		if(simIsRunning.get()) {
     
    			//======================================
    			//switch case for intersection 1 colors
    			//======================================
    			switch(intersection1.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 1st intersection and stop at 989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 989 && i.getPosition() < 1000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//==================================
    				//green light. Keep driving through
    				//==================================
    				for(Car i:carArray) {
     					if(i.atStopLight.get()) {
    						 i.resume();
    					}
    				}
    				break;
    			}
     
    			//======================================
    			//switch case for intersection 2 colors
    			//======================================
    			switch(intersection2.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 2nd intersection and stop at 1989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 1989 && i.getPosition() < 2000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//==================================
    				//green light. Keep driving through
    				//==================================
    				for(Car i:carArray) {
    					if(i.atStopLight.get()) {
    						i.resume();
    					}
    				}
    				break;
    			}
     
    			//======================================
    			//switch case for intersection 3 colors
    			//======================================
    			switch(intersection3.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 2nd intersection and stop at 2989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 2989 && i.getPosition() < 3000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//===================================
    				//green light. Keep driving through.
    				//loops back to beginning of track
    				//===================================
    				for(Car i:carArray) {
     
    					if(i.atStopLight.get()) {
    						i.resume();
    					}
    				}
    				break;
    			}
    		}
    	}
     
    	//=============================================================
    	//adjusts JSlider values and car speeds as their states change
    	//=============================================================
    	@Override
    	public void stateChanged(ChangeEvent e) {
    		try {
    			trafficData[0][1] = car1Slider.getValue();
    			trafficData[1][1] = car2Slider.getValue();
    			trafficData[2][1] = car3Slider.getValue();
     
    			trafficData[0][3] = car1.getSpeed() + " km/h";
    			trafficData[1][3] = car2.getSpeed() + " km/h";
    			trafficData[2][3] = car3.getSpeed() + " km/h";
    		}
    		catch(NullPointerException npe) {
    			npe.printStackTrace();
    		}
     
    		dataTable.repaint();		
    	}
     
    	//====================================
    	//constantly update JSliders position
    	//====================================
    	@Override
    	public void run() {
     
    		while(running) {
     
    			if(simIsRunning.get()) {
    				car1Slider.setValue(car1.getPosition());
    				car2Slider.setValue(car2.getPosition());
    				car3Slider.setValue(car3.getPosition());
    				getData();
    			}
    		}
     
    	}
    }


    Time.java
    /**
    * Time retrieves the current time EST and formats it to display
    * hours:minutes:seconds and runs it concurrently while the
    * program is running using a thread
    */
     
    //=========
    //packages
    //=========
    import java.text.*;
    import java.util.*;
     
    //====================
    //start of class time
    //====================
    public class Time implements Runnable {
     
    	//==========
    	//variables
    	//==========
    	private boolean running;
    	private String timePattern = "hh:mm:ss a";
     
    	//=========
    	//invoking
    	//=========
    	private Date date = new Date(System.currentTimeMillis());	
    	private SimpleDateFormat timeFormat = new SimpleDateFormat(timePattern);	
     
    	//============
    	//constructor
    	//============
    	public Time() {
    		this.running = Thread.currentThread().isAlive();
    	}
     
    	//==========================================
    	//retrieves the current time and returns it 
    	//==========================================
    	public String getTime() {
    		date = new Date(System.currentTimeMillis());
    		return timeFormat.format(date);
    	}
     
    	//========================================================
    	//constantly update current time while program is running
    	//========================================================
    	@Override
    	public void run() {
    		while (running) {
    			TrafficMitigation.currentTime.setText(getTime());
    		}
    	}
    }


    Intersection.java
    /**
    * Intersection controls the traffic lights at the 3 intersections spaced
    * apart on JSlider(0, 3000) evenly at 1000 intervals. Cars can pass through
    * the intersections on Green and Yellow but cant pass on Red
    */
     
     
    //=========
    //packages
    //=========
    import java.awt.*;
    import javax.swing.*;
    import java.util.concurrent.atomic.*;
     
    //============================
    //start of class Intersection
    //============================
    public class Intersection implements Runnable {
     
    	//======
    	//array
    	//======
    	private final String[] COLORS = {"Green", "Yellow", "Red"};
     
    	//==========
    	//variables
    	//==========	
    	private JLabel jLabel;
    	private int i = 0;
    	private Thread thread;	
    	private String currentLight = COLORS[i];
    	private String threadName;
     
    	//=========
    	//invoking
    	//=========
    	private static final AtomicBoolean running = new AtomicBoolean(false);
    	public static final AtomicBoolean paused = new AtomicBoolean(false);
     
    	//============
    	//constructor
    	//============
    	public Intersection(String name, JLabel j) {
    		this.threadName = name;
    		this.jLabel = j;
    		System.out.println("Creating " + threadName);
    	}
     
    	//=================================================
    	//synchornizes getting traffic light colors: G,Y,R
    	//=================================================
    	public synchronized String getColor() {
    		this.currentLight = COLORS[i];
    		return this.currentLight;
    	}
     
    	//============================
    	//pauses and displays threads
    	//============================
    	public void paused() {
    		paused.set(true);
    		System.out.println("Pausing " + threadName);
    	}
     
    	//===========================================
    	//synchronizes and displays resuming threads
    	//===========================================
    	public synchronized void resume() {
    		paused.set(false);
    		notify();
    		System.out.println("Resuming " + threadName);
    	}
     
    	//============================
    	//starts and displays threads
    	//============================
    	public void start() {
    		System.out.println("Starting " + threadName);
    		if(thread == null) {
    			thread = new Thread(this, threadName);
    			thread.start();
    		}
    	}
     
    	//=============================================
    	//stops and displays threads using interrupt()
    	//=============================================
    	public void stop() {
    		thread.interrupt();
    		running.set(false);
    		System.out.println("Stopping " + threadName);
    	}
     
    	//===================
    	//interrupts threads
    	//===================
    	public void interrupt() {
    		thread.interrupt();
    	}
     
    	//========================
    	//display running threads
    	//========================
    	@Override
    	public void run() {
    		System.out.println("Running " + threadName);
    		running.set(true);
     
    		//========================
    		//while thread is running
    		//========================
    		while(running.get()) {
     
    			//===================
    			//try to synchronize
    			//===================
    			try {
    				synchronized(this) {
     
    					//=======================
    					//while thread is paused
    					//=======================
    					while(paused.get()) {
    						System.out.println(threadName + " waiting");
    						wait();
    					}
    				}
     
    				//==========================================
    				//switch case for changing the color of the
    				//lights and putting threads to sleep
    				//==========================================
    				switch (getColor()) {
    				case "Green":
    					//==========================================
    					//green light. goes to sleep for 10 seconds
    					//increments 'i' so the next light color is
    					//yellow
    					//==========================================
    					jLabel.setForeground(Color.GREEN.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(10000);
    					i++;
    					break;
    				case "Yellow":
    					//==========================================
    					//yellow light. Goes to sleep for 5 seconds
    					//increments 'i' so the next light color is
    					//red
    					//==========================================
    					jLabel.setForeground(Color.YELLOW.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(5000);
    					i++;
    					break;
    				case "Red":
    					//=======================================
    					//red light. Goes to sleep for 5 seconds
    					//sets 'i' back to zero so the next 
    					//light color is green
    					//=======================================
    					jLabel.setForeground(Color.RED.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(5000);
    					i = 0;
    					break;
    				default:
    					break;
    				}
    			} 
    			catch(NullPointerException npe) {
    				npe.printStackTrace();
    			}
    			catch(InterruptedException ex) {
    				paused.set(true);
    			}
    		}
    	}
    }


    Car.java
    /**
    * Car controls the speed of the cars, the x-position and thread
    * starting, pausing, resuming and stopping 
    */
     
    //=========
    //packages
    //=========
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.*;
     
    //===================
    //start of class Car
    //===================
    public class Car implements Runnable {
     
    	//==========
    	//variables
    	//==========
    	private int xPosition;
    	private int speed = 0;
     
    	private String threadName = "";
     
    	public Thread thread;
     
    	//=========
    	//invoking
    	//=========
    	private final AtomicBoolean running = new AtomicBoolean(false);
     
    	public final AtomicBoolean atStopLight = new AtomicBoolean(false);
    	public final AtomicBoolean paused = new AtomicBoolean(false);
     
    	//============
    	//constructor
    	//============
    	public Car(String name, int max, int min) {
    		this.threadName = name;
    		this.xPosition = ThreadLocalRandom.current().nextInt(min, max);
    		System.out.println("Creating " + threadName);
    	}
     
    	//=====================================
    	//determines position of the cars at x
    	//=====================================
    	public synchronized int getPosition() {
    		return xPosition;
    	}
     
    	//========================================
    	//calculates the speed of the cars in kph
    	//========================================
    	public int getSpeed() {
     
    		if(running.get()) {
     
    			if(atStopLight.get()) {
    				speed = 0;
    			}
    			else {
    				speed = 3 * 60;
    			}
    		} 
    		else
    			speed = 0;
    		return speed;
    	}
     
    	//==========================
    	//displays threads starting
    	//==========================
    	public void start() {
    		System.out.println("Starting " + threadName);
     
    		if(thread == null) {
    			thread = new Thread(this, threadName);
    			thread.start();
    		}
    	}
     
    	//==========================
    	//displays threads stopping
    	//==========================
    	public void stop() {
    		thread.interrupt();
    		running.set(false);
    		System.out.println("Stopping " + threadName);
    	}
     
    	//=========================
    	//displays threads pausing
    	//=========================
    	public void paused() {
    		paused.set(true);
    		System.out.println("Paused " + threadName);
    	}
     
    	//==========================
    	//displays threads resuming
    	//==========================
    	public synchronized void resume() {
     
    		if(paused.get() || atStopLight.get()) {
    			paused.set(false);
    			atStopLight.set(false);
    			notify();
    			System.out.println("Resuming " + threadName);
    		}
    	}
     
    	//=========================
    	//displays threads running
    	//=========================
    	@Override
    	public void run() {
    		System.out.println("Running " + threadName);
    		running.set(true);
     
    		//===============================
    		//while AtomicBoolean is running
    		//===============================
    		while(running.get()) {
    			try {
     
    				//=============================
    				//while cars x position < 3000
    				//=============================
    				while(xPosition < 3000) {
    					synchronized(this) {
     
    						//==============================
    						//while paused or at stop light
    						//==============================
    						while(paused.get() || atStopLight.get()) {
    							System.out.println(threadName + " waiting");
    							wait();
    						}
    					}
     
    					//=======================
    					//Check if still running
    					//=======================
    					if(running.get()) {
    						Thread.sleep(100);
    						xPosition += 5;
    					}
    				}
    				xPosition = 0;
    			} 
    			catch(InterruptedException ie) {
    				return;
    			}
    		}
    	}
    }
    Last edited by EasyE; December 14th, 2021 at 07:48 PM.

  2. #2
    Junior Member
    Join Date
    Sep 2021
    Posts
    11
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Traffic Light program and Threads problem

    Edit: I may have put this in the wrong thread previously


    I need help starting a new thread after I stop the current thread. I keep getting IllegalThreadStateException and I don't know how to fix it.

    Exception happens at TrafficMitigation.lambda$0(TrafficMitigation.java: 270)

    TrafficMitigation.java
    /**
    * 
    * TrafficMitigation is a GUI application that runs multiple threads concurrently
    * to display current time stamps in 1 second intervals and a real-time Traffic 
    * light display for three major intersections using multiple JFrame components
    * and listeners
    */
     
    //=========
    //packages
    //=========
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import java.util.concurrent.atomic.*;
     
    //=================================
    //start of class TrafficMitigation
    //=================================
    public class TrafficMitigation extends JFrame implements Runnable, ChangeListener {
    	private static final long serialVersionUID = 1L;
     
    	//==================
    	//JFrame components
    	//==================
    	public static JLabel currentTime = new JLabel();
     
    	public static JLabel trafficLight1 = new JLabel();
    	private static JLabel trafficLightLabel1;
     
    	public static JLabel trafficLight2 = new JLabel();
    	private static JLabel trafficLightLabel2;
     
    	public static JLabel trafficLight3 = new JLabel();
    	private static JLabel trafficLightLabel3;
     
    	private static JButton startButton;
    	private static JButton pauseButton;
    	private static JButton stopButton;
     
    	private static JComboBox<Integer> carsJComboBox;
    	private static JComboBox<Integer> intersectionJComboBox;
     
    	private static JLabel carBoxLabel;
    	private static JLabel intersectionBoxLabel;
    	private static JLabel header1;
    	private static JLabel header2;
    	private static JLabel timeLabel;	
     
    	private static JSlider car1Slider;
    	private static JSlider car2Slider;
    	private static JSlider car3Slider;
     
    	//==========
    	//variables
    	//==========
    	private static boolean running;
    	private static Thread thread;
     
    	//=========
    	//invoking
    	//=========
    	private static final AtomicBoolean simIsRunning = new AtomicBoolean(false);
     
    	//=============================================
    	//invoking and each object gets its own thread
    	//=============================================
    	private Intersection intersection1 = new Intersection("Intersection 1 Thread", trafficLight1);
    	private Intersection intersection2 = new Intersection("Intersection 2 Thread", trafficLight2);
    	private Intersection intersection3 = new Intersection("Intersection 3 Thread", trafficLight3);
     
    	private Car car1 = new Car("Car 1 Thread", 1, 0);
    	private Car car2 = new Car("Car 2 Thread", 300, 0);
    	private Car car3 = new Car("Car 3 Thread", 700, 0);
     
    	//=======
    	//arrays
    	//=======
    	private Car[] carArray = {car1, car2, car3};
    	private Intersection[] intersectionArray = {intersection1, intersection2, intersection3};
    	private String[] tableColumnNames = {"Car", "X-Pos", "Y-Pos", "Speed km/h"};
     
    	//=========
    	//2D array
    	//=========
    	private Object[][] trafficData = {
    			{"Car 1", car1.getPosition(), 0, 0},
    			{"Car 2", car2.getPosition(), 0, 0},
    			{"Car 3", car3.getPosition(), 0, 0}
    	};
     
    	//============================
    	//JTable for all traffic data
    	//============================
    	private JTable dataTable = new JTable(trafficData, tableColumnNames);
     
    	//============================================
    	//displays GUI window to the user and passing
    	//it to class Thread. Also Giving the time
    	//its own thread
    	//============================================
    	public static void main(String args[]) {
            TrafficMitigation gui = new TrafficMitigation();        
     
            gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	    gui.setLocation(440, 100);
    	    gui.setResizable(false);
    	    gui.setSize(600, 700);
    	    gui.setVisible(true);
     
    	    thread = new Thread(gui);
     
    	    Thread time = new Thread(new Time());
    		time.start();
    	}
     
    	//==================================
    	//initializes JFrame components and
    	//adds them to the GUI window
    	//==================================
    	public TrafficMitigation() {
    		super("Traffic Congestion Mitigation Company");
     
    		setLayout(new GridLayout(9, 1));
     
    		trafficLightLabel1 = new JLabel("Traffic Light 1 (1000)");
    		trafficLightLabel2 = new JLabel("Traffic Light 2 (2000)");
    		trafficLightLabel3 = new JLabel("Traffic Light 3 (3000)");		
     
    		startButton = new JButton("Start"); 
    		pauseButton = new JButton("Pause");
    		stopButton = new JButton("Stop");
     
    		carsJComboBox = new JComboBox<>();
    		carsJComboBox.addItem(1);
    		carsJComboBox.addItem(2);
    		carsJComboBox.addItem(3);
    		carsJComboBox.addItem(4);
    		carsJComboBox.addItem(5);
     
    		intersectionJComboBox = new JComboBox<>();
    		intersectionJComboBox.addItem(1);
    		intersectionJComboBox.addItem(2);
    		intersectionJComboBox.addItem(3);
    		intersectionJComboBox.addItem(4);
    		intersectionJComboBox.addItem(5);
     
    		carBoxLabel = new JLabel("Number of Cars");
    		intersectionBoxLabel = new JLabel("Number of Intersections");
     
    		header1 = new JLabel("Welcome to the Traffic Congestion Mitigation Simulator", SwingConstants.CENTER);
    		header2 = new JLabel("\nClick the Start button to begin", SwingConstants.CENTER);
     
    		timeLabel = new JLabel("Current Time: ");		
     
    		car1Slider = new JSlider(0, 3000);
    		car1Slider.setMajorTickSpacing(1000);
    		car1Slider.setPaintTicks(true);
    		car1Slider.setPaintLabels(true);
     
    		car2Slider = new JSlider(0, 3000);
    		car2Slider.setMajorTickSpacing(1000);
    		car2Slider.setPaintTicks(true);
    		car2Slider.setPaintLabels(true);
     
    		car3Slider = new JSlider(0, 3000);
    		car3Slider.setMajorTickSpacing(1000);
    		car3Slider.setPaintTicks(true);
    		car3Slider.setPaintLabels(true);		
     
    		//=====================================
    		//JPanel's for headers, buttons, time,
    		//intersections, datatable
    		//=====================================
    		JPanel headerPanel = new JPanel();
    		headerPanel.setLayout(new GridLayout(2,1));
    		headerPanel.add(header1);
    		headerPanel.add(header2);
     
    		JPanel buttonPanel = new JPanel();
    		buttonPanel.add(startButton);
    		buttonPanel.add(pauseButton);
    		buttonPanel.add(stopButton);
     
    		JPanel timePanel = new JPanel();
    		timePanel.add(timeLabel);
    		timePanel.add(currentTime);
     
    		JPanel boxPanel = new JPanel();
    		boxPanel.add(carBoxLabel);
    		boxPanel.add(carsJComboBox);
    		boxPanel.add(intersectionBoxLabel);
    		boxPanel.add(intersectionJComboBox);
     
    		JPanel lightPanel = new JPanel();
       		lightPanel.add(trafficLightLabel1);
    		lightPanel.add(trafficLight1);
    		lightPanel.add(trafficLightLabel2);
    		lightPanel.add(trafficLight2);
    		lightPanel.add(trafficLightLabel3);
    		lightPanel.add(trafficLight3);
     
    		JScrollPane scrollPane = new JScrollPane(dataTable);
    		JPanel dataPanel = new JPanel();
    		dataPanel.add(scrollPane);		
     
    		//================================
    		//add JPanels and JSliders to GUI
    		//================================
    		add(headerPanel);
    		add(timePanel);
    		add(boxPanel);
    		add(buttonPanel);
    		add(lightPanel);
    		add(car1Slider);
    		add(car2Slider);
    		add(car3Slider);
    		add(dataPanel);
     
    		//==============================================
    		//add ChangeListners and set Values to JSliders
    		//==============================================
    		car1Slider.addChangeListener(this);
    		car1Slider.setValue(car1.getPosition());
    		car2Slider.addChangeListener(this);
    		car2Slider.setValue(car2.getPosition());
    		car3Slider.addChangeListener(this);				
    		car3Slider.setValue(car3.getPosition());
    		setButtons();
     
    		//=========================
    		//setting JTable Viewports
    		//=========================
    		dataTable.setPreferredScrollableViewportSize(new Dimension(400, 300));
    		dataTable.setFillsViewportHeight(true);
     
    		//===================
    		//run current thread
    		//===================
    		running = Thread.currentThread().isAlive();		
    	}
     
    	//==================================
    	//handles all JButtons ActionEvents
    	//==================================
    	private void setButtons() {
     
    		//====================================
    		//handles "Start" buttton ActionEvent
    		//====================================
    		startButton.addActionListener((ActionEvent e) -> {
     
    			//=================================================================
    			//begin threads for cars and intersections when "Start" is clicked
    			//=================================================================
    			if(!simIsRunning.get()) {
    				System.out.println(Thread.currentThread().getName() + " calling start");
     
    				try {
    					intersection1.start();
    					intersection2.start();
    					intersection3.start();
    					car1.start();
    					car2.start();
    					car3.start();
    					thread.start();  /**Exception thrown here
     
     
     
     
     
     
                                                                   */
    				}
    				catch(IllegalThreadStateException itse) {
    					itse.printStackTrace();;
    				}
    				catch(NullPointerException npe) {
    					npe.printStackTrace();
    				}				
    			}
    			simIsRunning.set(true);
    		});
     
    		//===================================
    		//handles "Pause" button ActionEvent
    		//===================================
    		pauseButton.addActionListener((ActionEvent e) -> {
     
    			if(simIsRunning.get()) {
     
    				//=====================================================
    				//loop through cars and intersections to call paused()
    				//=====================================================
    				for(Car i: carArray) {
    					i.paused();
    					System.out.println(Thread.currentThread().getName() + " calling pause");
    				}
     
    				//=======================================
    				//for loop to interrupt sleeping threads
    				//=======================================
    				for(Intersection i: intersectionArray) {
    					i.interrupt();
    					i.paused();
    				}
     
    				//=========================================
    				//-changes pause button text to "Continue"
    				//-sets simluation running to false
    				//=========================================
    				pauseButton.setText("Continue");
    				simIsRunning.set(false);
    			} 
     
    			//=============================
    			//"Continue" button is clicked
    			//=============================
    			else {
     
    				//====================================
    				//for loop to resume threads for cars
    				//====================================
    				for(Car i:carArray) {
     
    					if(i.paused.get()) {
    						i.resume();
    						System.out.println(Thread.currentThread().getName() + " calling resume");
    					}
    				}
     
    				//=============================================
    				//for loop to resume threads for intersections
    				//=============================================
    				for(Intersection i: intersectionArray) {
    					i.resume();
    				}
     
    				//=========================================
    				//-changes continue button text to "Pause"
    				//-sets simluation running to true
    				//=========================================
    				pauseButton.setText("Pause");
    				simIsRunning.set(true);
    			}
    		});
     
    		//==================================
    		//handles "Stop" button ActionEvent
    		//==================================
    		stopButton.addActionListener((ActionEvent e) -> {
     
    			if(simIsRunning.get()) {
    				System.out.println(Thread.currentThread().getName() + " calling stop");
     
    				//=================================
    				//for loop to stop all car threads
    				//=================================
    				for(Car i: carArray) {
    					i.stop();
    				}
     
    				//==========================================
    				//for loop to stop all intersection threads
    				//==========================================
    				for(Intersection i: intersectionArray) {
    					i.stop();
    				}
    				simIsRunning.set(false);
    			}
    		});
    	}
     
    	//=========================================
    	//checks status of the intersection. Stops
    	//car threads for running the lights
    	//=========================================
    	private void getData() {
    		if(simIsRunning.get()) {
     
    			//======================================
    			//switch case for intersection 1 colors
    			//======================================
    			switch(intersection1.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 1st intersection and stop at 989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 989 && i.getPosition() < 1000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//==================================
    				//green light. Keep driving through
    				//==================================
    				for(Car i:carArray) {
     					if(i.atStopLight.get()) {
    						 i.resume();
    					}
    				}
    				break;
    			}
     
    			//======================================
    			//switch case for intersection 2 colors
    			//======================================
    			switch(intersection2.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 2nd intersection and stop at 1989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 1989 && i.getPosition() < 2000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//==================================
    				//green light. Keep driving through
    				//==================================
    				for(Car i:carArray) {
    					if(i.atStopLight.get()) {
    						i.resume();
    					}
    				}
    				break;
    			}
     
    			//======================================
    			//switch case for intersection 3 colors
    			//======================================
    			switch(intersection3.getColor()) {
    			case "Red":
    				//=====================================
    				//red light. Cars drive all the way up
    				//to 2nd intersection and stop at 2989
    				//=====================================
    				for(Car i: carArray) {
     
    					if(i.getPosition() > 2989 && i.getPosition() < 3000) {
    						i.atStopLight.set(true);
    					}
    				}
    				break;
    			case "Green":
    				//===================================
    				//green light. Keep driving through.
    				//loops back to beginning of track
    				//===================================
    				for(Car i:carArray) {
     
    					if(i.atStopLight.get()) {
    						i.resume();
    					}
    				}
    				break;
    			}
    		}
    	}
     
    	//=============================================================
    	//adjusts JSlider values and car speeds as their states change
    	//=============================================================
    	@Override
    	public void stateChanged(ChangeEvent e) {
    		try {
    			trafficData[0][1] = car1Slider.getValue();
    			trafficData[1][1] = car2Slider.getValue();
    			trafficData[2][1] = car3Slider.getValue();
     
    			trafficData[0][3] = car1.getSpeed() + " km/h";
    			trafficData[1][3] = car2.getSpeed() + " km/h";
    			trafficData[2][3] = car3.getSpeed() + " km/h";
    		}
    		catch(NullPointerException npe) {
    			npe.printStackTrace();
    		}
     
    		dataTable.repaint();		
    	}
     
    	//====================================
    	//constantly update JSliders position
    	//====================================
    	@Override
    	public void run() {
     
    		while(running) {
     
    			if(simIsRunning.get()) {
    				car1Slider.setValue(car1.getPosition());
    				car2Slider.setValue(car2.getPosition());
    				car3Slider.setValue(car3.getPosition());
    				getData();
    			}
    		}
     
    	}
    }


    Time.java
    /**
    * Time retrieves the current time EST and formats it to display
    * hours:minutes:seconds and runs it concurrently while the
    * program is running using a thread
    */
     
    //=========
    //packages
    //=========
    import java.text.*;
    import java.util.*;
     
    //====================
    //start of class time
    //====================
    public class Time implements Runnable {
     
    	//==========
    	//variables
    	//==========
    	private boolean running;
    	private String timePattern = "hh:mm:ss a";
     
    	//=========
    	//invoking
    	//=========
    	private Date date = new Date(System.currentTimeMillis());	
    	private SimpleDateFormat timeFormat = new SimpleDateFormat(timePattern);	
     
    	//============
    	//constructor
    	//============
    	public Time() {
    		this.running = Thread.currentThread().isAlive();
    	}
     
    	//==========================================
    	//retrieves the current time and returns it 
    	//==========================================
    	public String getTime() {
    		date = new Date(System.currentTimeMillis());
    		return timeFormat.format(date);
    	}
     
    	//========================================================
    	//constantly update current time while program is running
    	//========================================================
    	@Override
    	public void run() {
    		while (running) {
    			TrafficMitigation.currentTime.setText(getTime());
    		}
    	}
    }


    Intersection.java
    /**
    * Intersection controls the traffic lights at the 3 intersections spaced
    * apart on JSlider(0, 3000) evenly at 1000 intervals. Cars can pass through
    * the intersections on Green and Yellow but cant pass on Red
    */
     
     
    //=========
    //packages
    //=========
    import java.awt.*;
    import javax.swing.*;
    import java.util.concurrent.atomic.*;
     
    //============================
    //start of class Intersection
    //============================
    public class Intersection implements Runnable {
     
    	//======
    	//array
    	//======
    	private final String[] COLORS = {"Green", "Yellow", "Red"};
     
    	//==========
    	//variables
    	//==========	
    	private JLabel jLabel;
    	private int i = 0;
    	private Thread thread;	
    	private String currentLight = COLORS[i];
    	private String threadName;
     
    	//=========
    	//invoking
    	//=========
    	private static final AtomicBoolean running = new AtomicBoolean(false);
    	public static final AtomicBoolean paused = new AtomicBoolean(false);
     
    	//============
    	//constructor
    	//============
    	public Intersection(String name, JLabel j) {
    		this.threadName = name;
    		this.jLabel = j;
    		System.out.println("Creating " + threadName);
    	}
     
    	//=================================================
    	//synchornizes getting traffic light colors: G,Y,R
    	//=================================================
    	public synchronized String getColor() {
    		this.currentLight = COLORS[i];
    		return this.currentLight;
    	}
     
    	//============================
    	//pauses and displays threads
    	//============================
    	public void paused() {
    		paused.set(true);
    		System.out.println("Pausing " + threadName);
    	}
     
    	//===========================================
    	//synchronizes and displays resuming threads
    	//===========================================
    	public synchronized void resume() {
    		paused.set(false);
    		notify();
    		System.out.println("Resuming " + threadName);
    	}
     
    	//============================
    	//starts and displays threads
    	//============================
    	public void start() {
    		System.out.println("Starting " + threadName);
    		if(thread == null) {
    			thread = new Thread(this, threadName);
    			thread.start();
    		}
    	}
     
    	//=============================================
    	//stops and displays threads using interrupt()
    	//=============================================
    	public void stop() {
    		thread.interrupt();
    		running.set(false);
    		System.out.println("Stopping " + threadName);
    	}
     
    	//===================
    	//interrupts threads
    	//===================
    	public void interrupt() {
    		thread.interrupt();
    	}
     
    	//========================
    	//display running threads
    	//========================
    	@Override
    	public void run() {
    		System.out.println("Running " + threadName);
    		running.set(true);
     
    		//========================
    		//while thread is running
    		//========================
    		while(running.get()) {
     
    			//===================
    			//try to synchronize
    			//===================
    			try {
    				synchronized(this) {
     
    					//=======================
    					//while thread is paused
    					//=======================
    					while(paused.get()) {
    						System.out.println(threadName + " waiting");
    						wait();
    					}
    				}
     
    				//==========================================
    				//switch case for changing the color of the
    				//lights and putting threads to sleep
    				//==========================================
    				switch (getColor()) {
    				case "Green":
    					//==========================================
    					//green light. goes to sleep for 10 seconds
    					//increments 'i' so the next light color is
    					//yellow
    					//==========================================
    					jLabel.setForeground(Color.GREEN.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(10000);
    					i++;
    					break;
    				case "Yellow":
    					//==========================================
    					//yellow light. Goes to sleep for 5 seconds
    					//increments 'i' so the next light color is
    					//red
    					//==========================================
    					jLabel.setForeground(Color.YELLOW.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(5000);
    					i++;
    					break;
    				case "Red":
    					//=======================================
    					//red light. Goes to sleep for 5 seconds
    					//sets 'i' back to zero so the next 
    					//light color is green
    					//=======================================
    					jLabel.setForeground(Color.RED.darker());
    					jLabel.setText(getColor());
    					Thread.sleep(5000);
    					i = 0;
    					break;
    				default:
    					break;
    				}
    			} 
    			catch(NullPointerException npe) {
    				npe.printStackTrace();
    			}
    			catch(InterruptedException ex) {
    				paused.set(true);
    			}
    		}
    	}
    }


    Car.java
    /**
    * Car controls the speed of the cars, the x-position and thread
    * starting, pausing, resuming and stopping 
    */
     
    //=========
    //packages
    //=========
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.*;
     
    //===================
    //start of class Car
    //===================
    public class Car implements Runnable {
     
    	//==========
    	//variables
    	//==========
    	private int xPosition;
    	private int speed = 0;
     
    	private String threadName = "";
     
    	public Thread thread;
     
    	//=========
    	//invoking
    	//=========
    	private final AtomicBoolean running = new AtomicBoolean(false);
     
    	public final AtomicBoolean atStopLight = new AtomicBoolean(false);
    	public final AtomicBoolean paused = new AtomicBoolean(false);
     
    	//============
    	//constructor
    	//============
    	public Car(String name, int max, int min) {
    		this.threadName = name;
    		this.xPosition = ThreadLocalRandom.current().nextInt(min, max);
    		System.out.println("Creating " + threadName);
    	}
     
    	//=====================================
    	//determines position of the cars at x
    	//=====================================
    	public synchronized int getPosition() {
    		return xPosition;
    	}
     
    	//========================================
    	//calculates the speed of the cars in kph
    	//========================================
    	public int getSpeed() {
     
    		if(running.get()) {
     
    			if(atStopLight.get()) {
    				speed = 0;
    			}
    			else {
    				speed = 3 * 60;
    			}
    		} 
    		else
    			speed = 0;
    		return speed;
    	}
     
    	//==========================
    	//displays threads starting
    	//==========================
    	public void start() {
    		System.out.println("Starting " + threadName);
     
    		if(thread == null) {
    			thread = new Thread(this, threadName);
    			thread.start();
    		}
    	}
     
    	//==========================
    	//displays threads stopping
    	//==========================
    	public void stop() {
    		thread.interrupt();
    		running.set(false);
    		System.out.println("Stopping " + threadName);
    	}
     
    	//=========================
    	//displays threads pausing
    	//=========================
    	public void paused() {
    		paused.set(true);
    		System.out.println("Paused " + threadName);
    	}
     
    	//==========================
    	//displays threads resuming
    	//==========================
    	public synchronized void resume() {
     
    		if(paused.get() || atStopLight.get()) {
    			paused.set(false);
    			atStopLight.set(false);
    			notify();
    			System.out.println("Resuming " + threadName);
    		}
    	}
     
    	//=========================
    	//displays threads running
    	//=========================
    	@Override
    	public void run() {
    		System.out.println("Running " + threadName);
    		running.set(true);
     
    		//===============================
    		//while AtomicBoolean is running
    		//===============================
    		while(running.get()) {
    			try {
     
    				//=============================
    				//while cars x position < 3000
    				//=============================
    				while(xPosition < 3000) {
    					synchronized(this) {
     
    						//==============================
    						//while paused or at stop light
    						//==============================
    						while(paused.get() || atStopLight.get()) {
    							System.out.println(threadName + " waiting");
    							wait();
    						}
    					}
     
    					//=======================
    					//Check if still running
    					//=======================
    					if(running.get()) {
    						Thread.sleep(100);
    						xPosition += 5;
    					}
    				}
    				xPosition = 0;
    			} 
    			catch(InterruptedException ie) {
    				return;
    			}
    		}
    	}
    }

  3. #3
    Super Moderator Norm's Avatar
    Join Date
    May 2010
    Location
    Eastern Florida
    Posts
    24,904
    Thanks
    58
    Thanked 2,692 Times in 2,642 Posts

    Default Re: Help with traffic light program and threads

    Have you read the API doc for the start method? It says:
    Throws:
    IllegalThreadStateException - if the thread was already started.
    If you don't understand my answer, don't ignore it, ask a question.

  4. #4
    Junior Member
    Join Date
    Sep 2021
    Posts
    11
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Help with traffic light program and threads

    My program runs and threads work, what is ThreadLocalRandom good for? Still learning Threads and Multithreading
    Last edited by EasyE; December 14th, 2021 at 10:08 PM.

  5. #5
    Super Moderator Norm's Avatar
    Join Date
    May 2010
    Location
    Eastern Florida
    Posts
    24,904
    Thanks
    58
    Thanked 2,692 Times in 2,642 Posts

    Default Re: Help with traffic light program and threads

    My program runs and threads work,
    Have you fixed your problem? What did you change?
    If you don't understand my answer, don't ignore it, ask a question.

Similar Threads

  1. How Java program executed? Heap, threads, stack, GC
    By Ram Lakshmanan in forum Java SE API Tutorials
    Replies: 0
    Last Post: May 10th, 2021, 05:12 AM
  2. Can a lot of threads slow down my program?
    By Gasis in forum Threads
    Replies: 4
    Last Post: June 15th, 2014, 02:03 PM
  3. Threads in BankAccount program
    By ben-der in forum What's Wrong With My Code?
    Replies: 22
    Last Post: March 15th, 2012, 10:19 AM
  4. Traffic Light problems
    By sircamolate in forum What's Wrong With My Code?
    Replies: 5
    Last Post: November 28th, 2011, 03:02 PM
  5. Replies: 5
    Last Post: October 17th, 2011, 07:43 AM