并发编程学习(四):wait()、nitify()
1、wait、notify原理
- Owner 线程发现条件不满足,调用wait()方法,即可进入WaitSet,变为WAITING 状态。
- BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间片。
- BLOCKED线程会在Owner线程释放锁时唤醒。
- WAITING线程会在Owner线程调用notify()或 notifyAll()时唤醒,但唤醒后并不意味着立刻获得锁,仍需进入EntryList重新竞争。
2、API介绍
线程通信:wait() / notify() / notifyAll() 此三个方法定义在Object类中的。
- obj.wait() 让进入object监视器的线程 到waitset等待。
- obj.notify()在object上正在waitSet等待的线程挑一个唤醒。
- obj.notifyAll()让object上正在waitSet等待的线程全部唤醒。
它们都是线程之间进行协作的手段,都属于Object对象的方法。必须获得此对象obj的锁,才能调用这几个方法。
wait() 方法会释放对象的锁,进入WaitSet等待区(无限制等待,直到notify为止),从而然其他线程有机会获取到对象锁。
wait(long n)有时限的等待,到n毫秒后结束等待,或是被notify。
未获得锁,直接调用wait()会报错。代码示例:
notify() / notifyAll()代码示例如下:
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized(obj) {
log.debug("执行...");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...");
}
},"t1").start();
new Thread(() -> {
synchronized(obj) {
log.debug("执行...");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...");
}
},"t2").start();
// 主线程两秒后执行
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("唤醒obj上的其它线程");
synchronized (obj) {
// obj.notify(); // 唤醒obj上随机一个线程
obj.notifyAll(); // 唤醒obj上所有等待线程
}
}
obj.notify()代码执行结果:
14:15:50.663 [t1] DEBUG com.example.common.MagicNumberUtilsTests - 执行...
14:15:50.663 [t2] DEBUG com.example.common.MagicNumberUtilsTests - 执行...
14:15:52.673 [main] DEBUG com.example.common.MagicNumberUtilsTests - 唤醒obj上的其它线程
14:15:52.673 [t1] DEBUG com.example.common.MagicNumberUtilsTests - 其它代码...
obj.notifyAll()代码执行结果:
14:09:31.945 [t1] DEBUG com.example.common.MagicNumberUtilsTests - 执行...
14:09:31.949 [t2] DEBUG com.example.common.MagicNumberUtilsTests - 执行...
14:09:33.959 [main] DEBUG com.example.common.MagicNumberUtilsTests - 唤醒obj上的其它线程
14:09:33.959 [t2] DEBUG com.example.common.MagicNumberUtilsTests - 其它代码...
14:09:33.959 [t1] DEBUG com.example.common.MagicNumberUtilsTests - 其它代码...
3、wait、notify的正确使用
3.1、sleep(long n) 和wait(long n) 的区别
- sleep是Thread中的静态方法;而wait是Object中的方法。
- sleep不需要强制和synchronized配合使用;但wait需要和synchronized一起使用。
- sleep在睡眠的同时,不会释放对象锁;但wait在等待的时候会释放对象锁。
- 都调用这两个方法后,调用的当前线程都会进入:TIMED_WAITING状态。
3.2、正确使用格式
final static Object lock = new Object();
// 干活线程
synchronized(lock) {
while(条件不成立) {
lock.wait();
}
// 干活
}
// 另一个线程
synchronized(lock) {
lock.notifyAll();
}