本文已参与「新人创作礼」活动,一起开启掘金创作之路。
基本介绍
我们之前学习的过程中浅显的了解过wiat/notify,但是没有系统的介绍过wait/notify,wait是使线程陷入等待 notify是随机唤醒一个被wait的线程。
原理
当一个线程获取锁后 但是发现自己不满足某些条件 不能执行锁住部分的代码块时 需要进入等待列表 直到满足条件时才会重新竞争线程
注意:
当一个线程获取锁后 但是发现自己不满足某些条件 不能执行锁住部分的代码块时 需要进入等待列表 直到满足条件时才会重新竞争线程
API介绍
obj.wait():wait方法让进入object监视器的线程到waitSet等待。wait后会释放对象锁,让其他线程竞争
obj.wait(Long timeout):wait的有时限方法,如果在时限内没有其他线程唤醒,则自己直接唤醒自己,若期间有别的线程唤醒那就正常唤醒。wait后会释放对象锁,让其他线程竞争
obj.notify():notify方法让正在waitSet等待的线程挑一个唤醒
obj.notifyAll():notifyAll方法让正在waitSet等待的线程全部唤醒
它们都是线程之间进行协作的手段,都属于Object对象方法,必须获取此对象的锁,才能调用这几个方法,如果不加锁直接调用这些方法会报错
notify与notifyAll的对比
@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
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();
// 主线程两秒后执行
sleep(0.5);
log.debug("唤醒 obj 上其它线程");
synchronized (obj) {
// obj.notify(); // 唤醒obj上一个线程
obj.notifyAll(); // 唤醒obj上所有等待线程
}
}
}
结果:
调用notify时:
- 23:11:55.798 c.TestWaitNotify [t1] - 执行…
- 23:11:55.801 c.TestWaitNotify [t2] - 执行…
- 23:11:56.300 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
- 23:11:56.300 c.TestWaitNotify [t1] - 其它代码…
调用notifyAll时:
- 23:12:26.195 c.TestWaitNotify [t1] - 执行…
- 23:12:26.198 c.TestWaitNotify [t2] - 执行…
- 23:12:26.699 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
- 23:12:26.699 c.TestWaitNotify [t2] - 其它代码…
- 23:12:26.699 c.TestWaitNotify [t1] - 其它代码… 解释:
可以看出notify是随机唤醒一个线程,notifyAll则是唤醒全部线程
ait与sleep方法的区别
区别
1.sleep是Thread的类方法,而wait是Object的对象方法
2.sleep不需要强制和synchronized配合使用,但是wait需要和synchronized一起用
3.sleep在睡眠的同时,不会释放对象锁,但wait在等待时会释放对象锁
4.无时限wait方法执行后 线程变为WAITING状态,有时限的wait方法与sleep方法执行后变为TIMED_WAITING状态
分析下面代码
@Slf4j(topic = "c.Test19")
public class Test19 {
static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
log.debug("获得锁");
try {
// Thread.sleep(2000);
lock.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
Sleeper.sleep(1);
synchronized (lock) {
log.debug("获得锁");
}
}
}
结果:
当调用sleep时的情况:
23:20:48.788 c.Test19 [t1] - 获得锁
当调用wait时的情况:
23:21:27.759 c.Test19 [t1] - 获得锁
23:21:28.768 c.Test19 [main] - 获得锁
解释:
上述结果说明sleep在暂停期间 不会释放锁 导致 这期间其他线程不能运行,而wait则可以释放锁