1. Introduction
2. Thread States
3. Wait-Notify Example

1. Introduction

Most of the time in current web frameworks, the developer is shielded from most multi-threaded issues like deadlock, race conditions etc. Developers tend not to concern themselves too much with these issues.

Although they are a rare occurrence, I believe that you should understand those problems - if only to avoid making some embarrassing mistakes.

2. Thread States

Thread States - State Diagram
Thread States - State Diagram

When a thread is created, it is in the New state until the start() method is invoked - the thread becomes Runnable .

The scheduler - and only the scheduler - picks one or more threads that can execute code, by doing this it makes the threads Running . It is also the scheduler that stops a running thread and puts it in the Runnable state to give each thread a fair amount of running time. The thread can call the yield() method which signals to the scheduler the thread is willing to be Runnable and let other threads run instead.

A thread can die when the run method completes. There is no external switch to kill off a thread. Well, there is the deprecated stop method, but that should never be used.

3. Wait-Notify Example

When using the wait/notify methods, we need an instance of Object , in Java this can be almost anything - in the example below we use the Runner class. But we could have used a String or just new Object() for example.

The wait(), notify() and notifyAll() methods are implemented on the class Object . A thread calls wait() and then stays in the Waiting state until another thread invokes a notifyAll() or notify() on the lock/monitor.

The methods wait(), notify(), notifyAll() should only be called when owning the monitor, i.e. when inside a synchronized block or method.

In the example we start 10 threads ("Runner 0" to "Runner 9"). Each thread acquires the lock and then prints the elements of the first array, then calls notifyAll followed by wait(...) .

After another thread invokes notifyAll() our thread can obtain the lock and run the statements following wait : printing the upper case letters and the "Finished" line.

package be.ooxs.examples.threads;

public class ThreadDemoWait {

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(new Runner("Runner " + i)).start();
		}
	}
}

class Runner implements Runnable {
	static String[] first = new String[] { "one", "two", "three", "four", "five" };

	static String[] second = new String[] { "A", "B", "C", "D", "E" };

	String name;

	Runner(String name) {
		this.name = name;
	}

	public void run() {
		Object lock = Runner.class;
		System.out.println("     Starting: " + name);
		synchronized (lock) {
			for (int i = 0; i < first.length; i++) {
				System.out.println(name + ":> " + first[i]);
			}
			try {
				lock.notifyAll();
				lock.wait();//last thread will wait here forever!!
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			for (int i = 0; i < second.length; i++) {
				System.out.println(name + ":> " + second[i]);
			}
		}
		System.out.println("     Finished: " + name);
	}
}

However, this program never terminates. The last thread - in the printout below that is thread 9 - invokes wait but can never continue since there are no more threads left to call notifyAll . You even have to terminate the program manually.

If possible use synchronized blocks and when needed the sleep(), join(), yield() methods to coordinate thread execution. They're higher level and less error prone.

     Starting: Runner 0
Runner 0:> one
Runner 0:> two
Runner 0:> three
Runner 0:> four
Runner 0:> five
     Starting: Runner 1
Runner 1:> one
Runner 1:> two
Runner 1:> three
Runner 1:> four
Runner 1:> five
Runner 0:> A
Runner 0:> B
Runner 0:> C
Runner 0:> D
Runner 0:> E
     Finished: Runner 0
     Starting: Runner 2
Runner 2:> one
Runner 2:> two
Runner 2:> three
Runner 2:> four
Runner 2:> five
Runner 1:> A
Runner 1:> B
Runner 1:> C
Runner 1:> D
Runner 1:> E
     Finished: Runner 1
     Starting: Runner 3
Runner 3:> one
Runner 3:> two
Runner 3:> three
Runner 3:> four
Runner 3:> five
Runner 2:> A
Runner 2:> B
Runner 2:> C
Runner 2:> D
Runner 2:> E
     Finished: Runner 2
     Starting: Runner 4
Runner 4:> one
Runner 4:> two
Runner 4:> three
Runner 4:> four
Runner 4:> five
Runner 3:> A
Runner 3:> B
Runner 3:> C
Runner 3:> D
Runner 3:> E
     Finished: Runner 3
     Starting: Runner 5
Runner 5:> one
Runner 5:> two
Runner 5:> three
Runner 5:> four
Runner 5:> five
Runner 4:> A
Runner 4:> B
Runner 4:> C
Runner 4:> D
Runner 4:> E
     Finished: Runner 4
     Starting: Runner 6
Runner 6:> one
Runner 6:> two
Runner 6:> three
Runner 6:> four
Runner 6:> five
Runner 5:> A
Runner 5:> B
Runner 5:> C
Runner 5:> D
Runner 5:> E
     Finished: Runner 5
     Starting: Runner 7
Runner 7:> one
Runner 7:> two
Runner 7:> three
Runner 7:> four
Runner 7:> five
Runner 6:> A
Runner 6:> B
Runner 6:> C
Runner 6:> D
Runner 6:> E
     Finished: Runner 6
     Starting: Runner 8
Runner 8:> one
Runner 8:> two
Runner 8:> three
Runner 8:> four
Runner 8:> five
Runner 7:> A
Runner 7:> B
Runner 7:> C
Runner 7:> D
Runner 7:> E
     Finished: Runner 7
     Starting: Runner 9
Runner 9:> one
Runner 9:> two
Runner 9:> three
Runner 9:> four
Runner 9:> five
Runner 8:> A
Runner 8:> B
Runner 8:> C
Runner 8:> D
Runner 8:> E
     Finished: Runner 8