0%

Java 线程间协作:wait、notify、notifyAll

方法定义

在 Java 中,在线程中调用 wait() 方法,将阻塞等待其他线程的通知(其他线程调用 notify() 方法或 notifyAll() 方法),在线程中调用 notify() 方法或 notifyAll() 方法,将通知其他线程从 wait() 方法处返回。

Object 是所有类的超类,它有 5 个方法组成了等待/通知机制的核心:notify()、notifyAll()、wait()、wait(long)和 wait(long,int)。

调用这些方法之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用 wait()方法。如果调用 wait()时,没有持有适当的锁,则抛出 IllegalMonitorStateException,它是 RuntimeException 的一个子类,因此,不需要 try-catch 结构。

wait()

public final void wait() throws InterruptedException,IllegalMonitorStateException

进入 wait()方法后,当前线程释放锁。在从 wait()返回前,线程与其他线程竞争重新获得锁。

notify()

public final native void notify() throws IllegalMonitorStateException

该方法用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个 wait() 状态的线程来发出通知,并使它等待获取该对象的对象锁,但不惊动其他同样在等待被该对象 notify 的线程们。

notify 后,当前线程不会马上释放该对象锁,wait 所在的线程并不能马上获取该对象锁,要等到程序退出 synchronized 代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁。

notifyAll()

public final native void notifyAll() throws IllegalMonitorStateException

notifyAll 使所有原来在该对象上 wait 的线程统统退出 wait 的状态(即全部被唤醒,不再等待 notify 或 notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll 线程退出调用了 notifyAll 的 synchronized 代码块的时候),他们就会去竞争。

如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

参考 [极客学院] 线程间协作:wait、notify、notifyAll

深入理解:锁池和等待池

锁池

假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个 synchronized 方法(或者 synchronized 块),由于这些线程在进入对象的 synchronized 方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

等待池

假设一个线程 A 调用了某个对象的 wait() 方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中。

唤醒线程

另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

参考:[知乎] Java中notify和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
public class WaitDemo {

static Object object = new Object();

static class Thread1 implements Runnable {

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + " gets lock");
System.out.println(Thread.currentThread().getName() + " invokes wait()");
object.wait();
System.out.println(Thread.currentThread().getName() + " gets lock again");
System.out.println(Thread.currentThread().getName() + " releases lock");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

static class Thread2 implements Runnable {

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
synchronized (object) {
System.out.println(Thread.currentThread().getName() + " gets lock");
System.out.println(Thread.currentThread().getName() + " invokes notify()");
object.notify();
System.out.println(Thread.currentThread().getName() + " releases lock");
}
}
}

public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
}

运行结果始终如下:

1
2
3
4
5
6
7
8
9
Thread-0 is running
Thread-0 gets lock
Thread-0 invokes wait()
Thread-1 is running
Thread-1 gets lock
Thread-1 invokes notify()
Thread-1 releases lock
Thread-0 gets lock again
Thread-0 releases lock

参考:[CSDN] JAVA线程间协作:wait.notify.notifyAll