今天看到开源中国上有这样一个问答:假设我有两个对象锁,对象A锁有5个线程在等待,对象B锁有3个线程在等待,对象A锁中的线程执行完,这时调用notifyAll,是唤醒了对象AB两个锁的全部的等待线程还是只唤醒了A锁的5个线程?
1. 方法文档解释
通过看该方法文档的解释,可以得出下面结论:
- notifyAll()中All的含义是所有的线程,而不是所有的锁,只能唤醒等待(调用wait()方法等待)同一个锁的所有线程。notifyAll()执行后,只有一个线程能得到锁,其他没有得到锁的线程会继续保持在等待状态。【notifyAll()只能释放一把锁。】
- notifyAll()必须在当前线程拥有监视器锁的情况下执行,否则将抛出异常IllegalMonitorStateException。
2. 代码解释
定义两个对象锁,每个对象分别有两个进程,先将对象一里线程A与线程B启动,进入等待状态;在把对象二里的线程C启动,也进入等待状态;接着将对象二里的线程D启动并唤醒其他线程。
package person.xsc.practice;
public class ThreadWaitAndNotifyAll implements Runnable{
private static Object object1 = new Object();
private static Object object2 = new Object();
@Override
public void run() {
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + "获得锁,进入等待状态");
try {
object1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行最后部分结束");
}
}
public static void main(String[] args) {
ThreadWaitAndNotifyAll runnable = new ThreadWaitAndNotifyAll();
Thread threadA = new Thread(runnable,"线程A");
Thread threadB = new Thread(runnable,"线程B");
Thread threadC=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + "获得锁,进入等待状态");
try {
object2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行结束");
}
}
},"线程D");
Thread threadD = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + "获得锁,开始通知唤醒所有线程");
// 唤醒其他线程
object2.notifyAll();
// 调用完notifyAll()方法后,同步代码块中的其他代码,必须执行完后才能将对象锁释放,而不是调用了notifyAll()方法后立即释放。
System.out.println(Thread.currentThread().getName() + "执行结束");
}
}
},"线程C");
// 每次运行,线程0和线程1的顺序可能会不同,执行顺序由CPU决定
threadA.start();
threadB.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadC.start();
threadD.start();
}
}
输出:
线程A获得锁,进入等待状态
线程B获得锁,进入等待状态
线程D获得锁,进入等待状态
线程C获得锁,开始通知唤醒所有线程
线程C执行结束
线程D执行结束
根据上面代码运行结果来看,确实如方法文档所说的那样:notifyAll()只能唤醒等待同一个锁的所有线程。