/ Java  

Java Multithreading 3: Wait and notify

In this blog I will introduce the thread wait/notify method. Contents include:

  1. wait(), notify(), notifyAll() and other methods
  2. wait() and notify()
  3. wait(long timeout) and notify()
  4. wait() and notifyAll()
  5. Why notify(), wait() and other functions are defined in Object instead of Thread

wait(), notify(), notifyAll() and other methods

Object.java defines interfaces such as wait(), notify() and notifyAll(). The role of wait() is to put the current thread into a wait state. At the same time, wait() will let the current thread release the lock it holds. The role of notify() and notifyAll() is to wake up the waiting thread on the current object; notify() will wake up a single thread, and notifyAll() will wake up all threads.

The detailed information about the wait/notify API in the Object class is as follows:

  • notify (): wakes up a single thread waiting on this object monitor.
  • notifyAll(): wake up all threads waiting on this object monitor.
  • wait(): puts the current thread in the blocked state (wait), until other threads call this object’s notify() method or notifyAll() method”, the current thread is woken up and enter the runnable state.
  • wait(long timeout): put the current thread in the blocked state (wait), until other threads call this object’s notify() method or notifyAll() method, or exceed the specified amount of time, the current thread is woken up and enter the runnable state.
  • wait(long timeout, int nanos): put the current thread in the blocked state (wait), until other threads call the notify() method or notifyAll() method of this object, or some other thread interrupts the current thread, or a certain amount of time has been exceeded, the current thread is woken up and enter the runnable state.

wait() and notify() example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class WaitAndNotify {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");

synchronized(t1) {
try {
// Start thread t1
System.out.println(Thread.currentThread().getName()+" start t1");
t1.start();

// Main thread waits for t1 to wake up via notify ()
System.out.println(Thread.currentThread().getName()+" wait()");
t1.wait();

System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class ThreadA extends Thread {
public ThreadA(String name) {
super(name);
}

public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName()+" call notify()");
// awake current wait thread
notify();
}
}
}

Results:

1
2
3
4
main start t1
main wait()
t1 call notify()
main continue

The following figure illustrates the flow of “main thread” and “thread t1”.



  • Note that “thread t1” stands for Thread t1 started in WaitAndNotify. And “lock” stands for synchronization lock of t1.
  • Main thread creates a new thread t1 through new ThreadA ("t1"). Then acquire synchronization lock of t1 through synchronized(t1). Then call t1.start() to start thread t1.
  • The main thread executes t1.wait() to release the t1 object’s lock and enters the blocked state (wait). Wait for the thread on the t1 object to wake it up via notify() or notifyAll().
  • After thread t1 runs, it acquire lock of current object through synchronized(this). Then call notify() to wake up “waiting thread on current object”, that is, wake up main thread.
  • After thread t1 is finished, release lock of current object. Immediately after that, the main thread acquires the t1 object’s lock and continues running.

For the code above, t1.wait() should let thread t1 wait; but why did it let main thread wait?

Let’s look at the comments of wait() in JDK:

Causes the current thread to wait until it is awakened, typically by being notified or interrupted.

Note: In the comments, wait() is to let the current thread wait, and the current thread refers to the thread running on the CPU!

This means that although t1.wait() is the wait() method called through thread t1, the place where t1.wait() is called is in main thread. The main thread must be the current thread, which is in running state to execute t1.wait(). Therefore, the current thread at this time is the main thread! Therefore, t1.wait() makes the main thread wait, not thread t1!

wait(long timeout) and notify()

wait(long timeout) will make the current thread in the blocked state (wait), until other threads call this object’s notify() method or notifyAll() method, or exceed the specified amount of time, the current thread is woken up and enter runnable state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class WaitTimeout {
public static void main(String[] args) {
ThreadB t1 = new ThreadB("t1");

synchronized(t1) {
try {
// start Thread t1
System.out.println(Thread.currentThread().getName() + " start t1");
t1.start();

// The main thread waits for t1 to wake up via notify() or notifyAll()
// or a delay of more than 3000ms; then it is woken up.
System.out.println(Thread.currentThread().getName() + " call wait ");
t1.wait(3000);

System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class ThreadB extends Thread {
public ThreadB(String name) {
super(name);
}

public void run() {
System.out.println(Thread.currentThread().getName()+" run");

// infinite loop
while(true)
;
}
}

Results:

1
2
3
4
main start t1
main call wait
t1 run // After around 3 seconds...output "main continue"
main continue

The following figure illustrates the flow of “main thread” and “thread t1”.



  • The main thread executes t1.start() to start thread t1.
  • The main thread executes t1.wait(3000). At this time, the main thread enters the blocked state (wait). Need the thread used for t1 object lock to wake it up through notify() or notifyAll() or after a timeout of 3000ms, the main thread enters the runnable state before it can run.
  • After thread t1 runs, it enters an endless loop and runs continuously.
  • After a timeout of 3000ms, the main thread will enter the runnable state, and then enter the running state.

wait() and notifyAll()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class NotifyAll {
private static Object obj = new Object();

public static void main(String[] args) {
ThreadC t1 = new ThreadC("t1");
ThreadC t2 = new ThreadC("t2");
ThreadC t3 = new ThreadC("t3");
t1.start();
t2.start();
t3.start();

try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized(obj) {
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}

static class ThreadC extends Thread{
public ThreadC(String name){
super(name);
}

public void run() {
synchronized (obj) {
try {
System.out.println(Thread.currentThread().getName() + " wait");

// awake current wait thread
obj.wait();

System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

Results:

1
2
3
4
5
6
7
8
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue


  • main thread created and started t1, t2 and t3.
  • The main thread sleeps for 3 seconds through sleep(3000). In the process of main thread sleeping, we assume that the three threads t1, t2 and t3 are all running. Take t1 as an example, when it is running, it will execute obj.wait() to wait for other threads to wake it up via notify() or nofityAll(). The same reason, t2 and t3 will also wait for other threads to wake them up via nofity() or nofityAll().
  • After the main thread sleeps for 3 seconds, it continue runs and execute obj.notifyAll() to wake up the waiting thread on obj, that is, wake up the three threads t1, t2 and t3. Immediately after the synchronized(obj) of the main thread is finished, the main thread releases the obj lock. In this way, t1, t2 and t3 can acquire the obj lock and continue to run!

Why notify(), wait() and other functions are defined in Object instead of Thread

The wait(), notify() and other functions in Object, like synchronized, will operate on “object synchronization lock”.

wait() will make the current thread wait, because the thread enters the blocked state (wait), so the thread should release the synchronization lock it holds, otherwise other threads cannot obtain the synchronization lock and cannot run!

OK, after the thread calls wait(), it releases the synchronous lock it holds. According to previous introduction, we know that the waiting thread can be woken up by notify() or notifyAll(). Now, think about a question: on what basis does notify() wake up the waiting thread? In other words, what is the relationship between wait() and notify()? The answer is: according to object synchronization lock.

The thread responsible for awakening the waiting thread (we call it “awakening thread”), it is only acquiring the object synchronization lock (the synchronization lock here must be the same as the synchronization lock of the waiting thread), and calling notify() or notifyAll() method, the waiting thread can be woken up. Although, the waiting thread is awakened, it cannot be executed immediately because the awakening thread also holds the object synchronization lock. You must wait for the awakening thread to release the object synchronization lock before the waiting thread can acquire the object synchronization lock and continue to run.

In short, notify(), wait() depends on object synchronization lock, and object synchronization lock is held by an object, and each object has only one lock! This is why notify(), wait() and other functions are defined in the Object class, not the Thread class.