当前位置:网站首页>并发之wait/notify说明
并发之wait/notify说明
2022-06-28 04:29:00 【七国的天下,我要九十九】
并发之wait/notify说明
1 wait/notify的原理

- Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
- BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
- BLOCKED 线程会在 Owner 线程释放锁时唤醒
- WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入 EntryList 重新竞争
2 wait/notify的API
obj.wait() 让进入 object 监视器的线程到 waitSet 等待
obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒**(随机)**
obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这些方法
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
// 让线程在obj上一直等待下去
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码....");
}
}).start();
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
// 让线程在obj上一直等待下去
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码....");
}
}).start();
// 主线程两秒后执行
sleep(2);
log.debug("唤醒 obj 上其它线程");
synchronized (obj) {
// 唤醒obj上一个线程 第一次测试
obj.notify();
// 唤醒obj上所有等待线程 第二次测试
// obj.notifyAll();
}
}
第一次测试notify结果
20:00:53.096 [Thread-0] c.TestWaitNotify - 执行....
20:00:53.099 [Thread-1] c.TestWaitNotify - 执行....
20:00:55.096 [main] c.TestWaitNotify - 唤醒 obj 上其它线程
20:00:55.096 [Thread-0] c.TestWaitNotify - 其它代码....
第二次测试notifyAll结果
19:58:15.457 [Thread-0] c.TestWaitNotify - 执行....
19:58:15.460 [Thread-1] c.TestWaitNotify - 执行....
19:58:17.456 [main] c.TestWaitNotify - 唤醒 obj 上其它线程
19:58:17.456 [Thread-1] c.TestWaitNotify - 其它代码....
19:58:17.456 [Thread-0] c.TestWaitNotify - 其它代码....
wait() 方法会释放对象的锁,进入 WaitSet 等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到 notify 为止.
wait(long n) 有时限的等待, 到 n 毫秒后结束等待,或是被 notify
3 wait/notify的使用
sleep(long n)和wait(long n)的区别
不同:
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要强制和 synchronized 配合使用,但 wait 需要 和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
相同:
- 它们 状态 TIMED_WAITING
案例1
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
sleep(2);
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
synchronized (room) {
log.debug("可以开始干活了");
}
}, "其它人").start();
}
sleep(1);
new Thread(() -> {
// 这里能不能加 synchronized (room)?
hasCigarette = true;
log.debug("烟到了噢!");
}, "送烟的").start();
/* 运行结果: 20:49:49.883 [小南] c.TestCorrectPosture - 有烟没?[false] 20:49:49.887 [小南] c.TestCorrectPosture - 没烟,先歇会! 20:49:50.882 [送烟的] c.TestCorrectPosture - 烟到了噢! 20:49:51.887 [小南] c.TestCorrectPosture - 有烟没?[true] 20:49:51.887 [小南] c.TestCorrectPosture - 可以开始干活了 20:49:51.887 [其它人] c.TestCorrectPosture - 可以开始干活了 20:49:51.887 [其它人] c.TestCorrectPosture - 可以开始干活了 20:49:51.888 [其它人] c.TestCorrectPosture - 可以开始干活了 20:49:51.888 [其它人] c.TestCorrectPosture - 可以开始干活了 20:49:51.888 [其它人] c.TestCorrectPosture - 可以开始干活了 */
说明:
- 其它干活的线程,都要一直阻塞,效率太低
- 小南线程必须睡足 2s 后才能醒来,就算烟提前送到,也无法立刻醒来
- 加了 synchronized (room) 后,就好比小南在里面反锁了门睡觉,烟根本没法送进门,main 没加 synchronized 就好像 main 线程是翻窗户进来的
- 解决方法,使用 wait - notify 机制
案例2
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
synchronized (room) {
log.debug("可以开始干活了");
}
}, "其它人").start();
}
sleep(1);
new Thread(() -> {
synchronized (room) {
hasCigarette = true;
log.debug("烟到了噢!");
room.notify();
}
}, "送烟的").start();
/* 运行结果: 20:51:42.489 [小南] c.TestCorrectPosture - 有烟没?[false] 20:51:42.493 [小南] c.TestCorrectPosture - 没烟,先歇会! 20:51:42.493 [其它人] c.TestCorrectPosture - 可以开始干活了 20:51:42.493 [其它人] c.TestCorrectPosture - 可以开始干活了 20:51:42.494 [其它人] c.TestCorrectPosture - 可以开始干活了 20:51:42.494 [其它人] c.TestCorrectPosture - 可以开始干活了 20:51:42.494 [其它人] c.TestCorrectPosture - 可以开始干活了 20:51:43.490 [送烟的] c.TestCorrectPosture - 烟到了噢! 20:51:43.490 [小南] c.TestCorrectPosture - 有烟没?[true] 20:51:43.490 [小南] c.TestCorrectPosture - 可以开始干活了 */
说明:
- 解决了其它干活的线程阻塞的问题
存在问题, 即存在其他线程等待的场景,不一定保证唤醒指定的线程.
案例3
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活...");
}
}
}, "小南").start();
new Thread(() -> {
synchronized (room) {
Thread thread = Thread.currentThread();
log.debug("外卖送到没?[{}]", hasTakeout);
if (!hasTakeout) {
log.debug("没外卖,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外卖送到没?[{}]", hasTakeout);
if (hasTakeout) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活...");
}
}
}, "小女").start();
sleep(1);
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外卖到了噢!");
room.notify();
}
}, "送外卖的").start();
/* 运行结果: 20:53:12.173 [小南] c.TestCorrectPosture - 有烟没?[false] 20:53:12.176 [小南] c.TestCorrectPosture - 没烟,先歇会! 20:53:12.176 [小女] c.TestCorrectPosture - 外卖送到没?[false] 20:53:12.176 [小女] c.TestCorrectPosture - 没外卖,先歇会! 20:53:13.174 [送外卖的] c.TestCorrectPosture - 外卖到了噢! 20:53:13.174 [小南] c.TestCorrectPosture - 有烟没?[false] 20:53:13.174 [小南] c.TestCorrectPosture - 没干成活... */
说明:
- notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线 程,称为 虚假唤醒
解决方法: notifyAll唤醒全部线程
案例4
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外卖到了噢!");
room.notifyAll();
}
}, "送外卖的").start();
/* 20:55:23.978 [小南] c.TestCorrectPosture - 有烟没?[false] 20:55:23.982 [小南] c.TestCorrectPosture - 没烟,先歇会! 20:55:23.982 [小女] c.TestCorrectPosture - 外卖送到没?[false] 20:55:23.982 [小女] c.TestCorrectPosture - 没外卖,先歇会! 20:55:24.979 [送外卖的] c.TestCorrectPosture - 外卖到了噢! 20:55:24.979 [小女] c.TestCorrectPosture - 外卖送到没?[true] 20:55:24.980 [小女] c.TestCorrectPosture - 可以开始干活了 20:55:24.980 [小南] c.TestCorrectPosture - 有烟没?[false] 20:55:24.980 [小南] c.TestCorrectPosture - 没干成活... */
说明:
- 用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新 判断的机会了
解决: 用 while + wait,当条件不成立,再次 wait
案例5
if (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 改为
while (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/* 运行结果: 20:58:34.322 [小南] c.TestCorrectPosture - 有烟没?[false] 20:58:34.326 [小南] c.TestCorrectPosture - 没烟,先歇会! 20:58:34.326 [小女] c.TestCorrectPosture - 外卖送到没?[false] 20:58:34.326 [小女] c.TestCorrectPosture - 没外卖,先歇会! 20:58:35.323 [送外卖的] c.TestCorrectPosture - 外卖到了噢! 20:58:35.324 [小女] c.TestCorrectPosture - 外卖送到没?[true] 20:58:35.324 [小女] c.TestCorrectPosture - 可以开始干活了 20:58:35.324 [小南] c.TestCorrectPosture - 没烟,先歇会! */
结构模式:
synchronized(lock) {
while(条件不成立) {
lock.wait();
}
// 干活
}
//另一个线程
synchronized(lock) {
lock.notifyAll();
}
边栏推荐
- The SQL of filincdc always reports this error when there are multiple tables. How can I solve it
- Idle interrupt cannot be cleared
- OracleData安装问题
- Project practice! Teach you JMeter performance test hand in hand
- 几百行代码实现一个脚本解释器
- 穿越封锁的最新利器,速度最快梯没有之一。
- Flexible IP network test tool -- x-launch
- S32ds jump to defaultisr
- Live online source code, JS dynamic effect, sidebar scrolling fixed effect
- To quickly download JDK, in addition to the official Oracle download, is there a download address for the latest version available in China
猜你喜欢

学习太极创客 — MQTT 第二章(四)ESP8266 保留消息应用

Audio and video technology development weekly

Audio and video technology development weekly

Difference between curdate() and now()

The number of small stores in Suning has dropped sharply by 428 in one year. Zhangkangyang, the son of Zhang Jindong, is the actual controller

mysql修改密码报错需要怎么做

CUPTI error: CUPTI could not be loaded or symbol could not be found.

10: 00 interview, came out at 10:02, the question is really too

How to traverse collections Ordereddict, taking it and forgetting items

大促场景下,如何做好网关高可用防护
随机推荐
Annual comprehensive analysis of China's audio market in 2022
Huawei's 9-year experience as a software testing director
Matlab exercises -- exercises related to symbolic operation
2022年G3锅炉水处理复训题库模拟考试平台操作
Source code of live video system, countdown display, countdown of commodity spike
Project practice! Teach you JMeter performance test hand in hand
[matlab traffic light identification] traffic light identification [including GUI source code 1908]
E-week finance Q1 mobile banking has 650million active users; Layout of financial subsidiaries in emerging fields
UI automation test framework construction - write an app automation
Severe tire damage: the first rock band in the world to broadcast live on the Internet
How do I get the STW (pause) time of a GC (garbage collector)?
有人用cdc同步到mysql发生过死锁吗?
Are test / development programmers really young? The world is fair. We all speak by strength
Unity delegate
易周金融 | Q1手机银行活跃用户规模6.5亿;理财子公司布局新兴领域
学习太极创客 — MQTT 第二章(四)ESP8266 保留消息应用
One article explains in detail | those things about growth
Detailed reading of the thesis: implementing volume models for handowriting text recognition
Sword finger offer 47 Maximum gift value (DP)
[csp-j2020] excellent splitting