一、为什么需要wait/notify?
我们都知道wati/notify用于线程间的同步。
假设有一把锁lock,此时线程t1来持有这把锁,但是由于其不满足业务条件暂时不能继续执行,如果t2此时来获取锁,发现锁仍被t1占有,则会发生阻塞,造成严重的效率问题,甚至出现死锁。
通过wait/notify,可是是t1先进入WAITING状态,等待t2的唤醒,被唤醒后再去执行对应的操作,这样就避免了t2线程的阻塞,且t1、t2之间的竞争也没有了,大大提升执行效率。
二、wait/notify原理
前面我们讲过Monitor(管程)的结构,其中有一块叫做waitSet的区域,里面存放的是状态为WAITING状态的线程。如下图虚线框中的内容:
涉及到Monitor,相信大家能够知道,只有在重量级锁中,才会有wait/notify。
下面按照上图简单分析下其流程:
- 假设线程thread1持有了重量级锁,即Owner,但是此时没有准备好,所以调用了wait方法。此时其状态就会变成WAITING,进入到Monitor的WaitSet当中。
- EntryList和WaitSet当中的线程,都是不占用CPU时间片的。
- EntryList当中的线程,会等待Owner执行完成后被唤醒。
- WaitSet当中的线程,会等待Owner执行notify或notifyAll方法进行唤醒,但是并不是直接获取到锁,而是被添加到EntryList当中进行非公平的竞争。
三、wait/notify使用
3.1 常用方法
- obj.wait() 让当前持有锁的Owner进入Monitor的WaitSet进行等待。
- obj.wait(long timeout) 让进入 object 监视器的线程到 waitSet 等待,可制定等待超时时间。
- obj.notify() Owner从正在 waitSet 等待的线程中挑一个唤醒。
- obj.notifyAll() Owner让正在 waitSet 等待的线程全部唤醒。
以上方法都是Object类的方法,必须要获取该对象锁,才能调用上述方法。
通过下面的例子,演示用法,妈妈正在做饭,爸爸和小A等着吃饭,爸爸发现饭没有做好,要去睡一会;小A发现饭没有做好,要去玩一会儿。妈妈做好饭了,叫小A和爸爸吃饭。饭后,小A去写作业了,爸爸去钓鱼了:
/**
* 吃了吗
*/
static boolean EATEN_YET = false;
static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
while (true) {
if (EATEN_YET) {
System.out.println(Thread.currentThread().getName() + ":我要开始写作业了!!");
break;
} else {
try {
System.out.println(Thread.currentThread().getName() + ":吃饭前我先玩一会儿!!");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "小A").start();
new Thread(() -> {
synchronized (lock) {
while (true) {
if (EATEN_YET) {
System.out.println(Thread.currentThread().getName() + ":我要去钓鱼了!!");
break;
} else {
try {
System.out.println(Thread.currentThread().getName() + ":吃饭前我先睡一会儿!!");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "爸爸").start();
new Thread(() -> {
synchronized (lock) {
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":开饭了!!");
EATEN_YET = true;
lock.notifyAll();
}
}, "妈妈").start();
}
结果:
小A:吃饭前我先玩一会儿!!
爸爸:吃饭前我先睡一会儿!!
妈妈:开饭了!!
爸爸:我要去钓鱼了!!
小A:我要开始写作业了!!