1.wait notify 原理
-
持有Owner的锁对象调用wait方法(obj.wait),就会使当前线程进入WaitSet中,变为WAITING状态。
-
处于BLOCKED和WAITING状态的线程都为阻塞状态,CPU都不会分给他们时间片。但是有所区别:
- BLOCKED状态的线程是在竞争对象时,发现Monitor的Owner已经是别的线程了,此时就会进入EntryList中,并处于BLOCKED状态
- WAITING状态的线程是获得了对象的锁,但是自身因为某些原因需要进入阻塞状态时,锁对象调用了wait方法而进入了WaitSet中,处于WAITING状态
-
BLOCKED状态的线程会在锁被释放的时候被唤醒,但是处于WAITING状态的线程只有被锁对象调用了notify方法(obj.notify/obj.notifyAll),才会被唤醒
-
WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争
只有使用了重量级锁才能调用wait方法:
public class Test1 {
final static Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
//只有在对象被锁住后才能调用wait方法
synchronized (LOCK) {
LOCK.wait();
}
}
}
API 介绍
-
obj.wait()
让进入object
监视器的线程到waitSet
等待 -
obj.notify()
在object
上正在waitSet
等待的线程中挑一个唤醒 -
obj.notifyAll()
让object
上正在waitSet
等待的线程全部唤醒
Wait与Sleep的区别
不同点
- Sleep是Thread类的静态方法,Wait是Object的方法,Object又是所有类的父类,所以所有类都有Wait方法。
- Sleep在阻塞的时候不会释放锁,而Wait在阻塞的时候会释放锁
- Sleep不需要与synchronized一起使用,而Wait需要与synchronized一起使用(对象被锁以后才能使用)
相同点
- 阻塞状态都为TIMED_WAITING
正确的使用wait
说到如何正确的使用wait,不如先谈谈为什么要正确的使用wait。
当然,这与我们的老朋友sleep是分不开的。
sleep在阻塞的时候,是不释放锁的,这就导致我们很多其他要通过这个锁对象对临界区进行操作的线程一时半会无法使用这个锁对象,导致无法执行代码,影响了效率。
所以我们要使用wait代替sleep。
但是不正确的使用wait,会导致虚假唤醒的产生。
虚假唤醒:因为唤醒wait只有notifyAll和notify两个方法,没有办法指定唤醒,所以常常会出现命名没达到某几个线程的唤醒条件,但是该线程被唤醒了,这就是虚假唤醒。
我们举个例子:
- 先说说sleep的模式
首先是sleep的情况,小南有烟才能干活,于是让他暂时等待,但是用sleep等待会影响后面线程的工作。
- 用了wait
解决了刚才的问题,可是如果wait的线程不只一个呢?
- 多个等待线程
此时小南的烟没到,却把他唤醒了,这就出现了问题,小南只有一次wait,之后就不能干活了。
我们要让他,无烟就一直等待。。
- while+notifyAll
此时就涉及了一种模式,后面我们会说。
大概的模式图是这样的
synchronized (LOCK) {
while(//不满足条件,一直等待,避免虚假唤醒) {
LOCK.wait();
}
//满足条件后再运行
}
synchronized (LOCK) {
//唤醒所有等待线程
LOCK.notifyAll();
}
2.模式之保护性暂停
public class Test2 {
public static void main(String[] args) {
String hello = "hello thread!";
Guarded guarded = new Guarded();
new Thread(()->{
System.out.println("想要得到结果");
synchronized (guarded) {
System.out.println("结果是:"+guarded.getResponse());
}
System.out.println("得到结果");
}).start();
new Thread(()->{
System.out.println("设置结果");
synchronized (guarded) {
guarded.setResponse(hello);
}
}).start();
}
}
class Guarded {
/**
* 要返回的结果
*/
private Object response;
//优雅地使用wait/notify
public Object getResponse() {
//如果返回结果为空就一直等待,避免虚假唤醒
while(response == null) {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return response;
}
public void setResponse(Object response) {
this.response = response;
synchronized (this) {
//唤醒休眠的线程
this.notifyAll();
}
}
@Override
public String toString() {
return "Guarded{" +
"response=" + response +
'}';
}
}