【JUC并发编程】 wait和notify的使用方式
在了解 wait
和 notify
的使用方式前,我们需要先明白 wait(long n)
和 sleep(long n)
的区别。
1. wait和sleep的区别
区别如下:
- sleep是Thread的静态方法,而wait是Object的成员方法。
- sleep不需要强制和
synchronized
配合使用, 但wait需要和synchronized
一起使用 - sleep在睡眠的时候不会释放对象锁,但wait在等待的时候会释放锁
- 它们睡眠/等待时的状态都为
TIMED_WAITING
案例:
用具体代码体会sleep和wait的区别。
public class Test {
static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
log.debug("获得锁");
try {
Thread.sleep(5000);
//lock.wait(5000);
log.debug("hhh");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
Sleeper.sleep(1);
synchronized (lock) {
log.debug("获得锁");
}
}
}
1)我们先注释 lock.wait(5000);
t1线程睡眠5s,主线程睡眠1s。在这5s内,t1线程一直没有释放锁,直到5s后结束睡眠。
运行结果如下:
2)我们再注释 Thread.sleep(5000);
取消 lock.wait(5000);
的注释。那么当t1线程执行wait方法时,直接就把锁给释放了。主线程就能立即获得锁。
运行结果如下:
2. wait和notify使用案例
通用资源
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
2.1 例1 解决虚假唤醒
notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线 程,称之为【虚假唤醒】。
而现在 WaitSet 中有两个线程,使用notify的话无法确定唤醒的是哪个线程。
解决方法:将主线程的 notify
方法替换成 notifyAll
方法。
public static void main(String[] args) {
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
while (!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);
while (!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.notifyAll();
}
}, "送外卖的").start();
}
2.2 例2 循环等待
用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新 判断的机会了。
解决方法:用while+wait,当条件不成立,再次wait
public static void main(String[] args) {
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
while (!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);
while (!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.notifyAll();
}
}, "送外卖的").start();
}