Wait/Notify
- Owner线程发现条件不满足,调用wait方法,即可进入WaitSet变为WAITING状态
- BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间片
- BLOCKED线程会在Owner线程释放锁时唤醒
- WAITING线程会在Owner线程调用notify或notifyAll时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList重新竞争
API
- 需要当前线程获取对象锁,才能调用
- notify 唤醒方法不可以指定唤醒
- LockSupport.park();
- LockSupport.unpark(Thread t);
- obj.wait():让进入object监视器的线程到waitSet等待
- 当调用无参wait会一直等待下去
- 调用有参的wait则会等待一段时间就会执行
- obj.notify():在object 上正在waitSet等待的线程中挑一个唤醒
- obj.notifyAll():唤醒全部的waitSet线程
java
static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
log.info("run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
log.info("run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2").start();
Thread.sleep(2);
synchronized (lock) {
lock.notifyAll();
}
}
- sleep(long n) 和 wait(long n) 的区别
- sleep是Thread方法,而wait是Object的方法
- sleep不需要强制和synchronized配合使用,但wait需要和synchronized-起用
- sleep在睡眠的同时,不会释放对象锁的,但wait在等待的时候会释放对象锁。
- 它们状态TIMED_ WAITING
park & unpark
java
// 暂停当前线程
LockSupport.park();
// 启动某个线程
LockSupport.unpark(Thread t);
特点
与Object的wait & notify相比
- wait, notify 和notifyAll必须配合Object Monitor一起使用,而unpark不必
- park & unpark是以线程为单位来[阻塞]和[唤醒]线程,而notify只能随机唤醒一个等待线程
- notifyAll是唤醒所有等待线程,就不那么[精确]
- park & unpark可以先unpark,而wait & notify不能先notify
原理
每个线程都有自己的一个Parker对象,由三部分组成_ counter, cond 和 mutex 打个比喻
- 线程就像一个旅人, Parker 就像他随身携带的背包,条件变量就好比背包中的帐篷。_counter 就好比背包
中的备用干粮(0 为耗尽,1 为充足)
- 调用park就是要看需不需要停下来歇息
- 如果备用干粮耗尽,那么钻进帐篷歇息
- 如果备用干粮充足,那么不需停留,继续前进
- 调用unpark,就好比令干粮充足
- 如果这时线程还在帐篷,就唤醒让他继续前进
- 如果这时线程还在运行,那么下次他调用park时,仅是消耗掉备用干粮,不需停留继续前进
- 因为背包空间有限,多次调用unpark仅会补充一份备用干粮
- 当前线程调用Unsafe.park(方法
- 检查_ counter ,本情况为0,这时,获得_ mutex 互斥锁
- 线程进入_ cond 条件变量阻塞
- 设置_ counter= 0