基本使用
park 和 unpark 是 LockSupport 类中的方法
// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(暂停线程对象);
park&unpark 与 wait¬ify 异同
- park和wait都会让线程进入
WAITING
或TIMED_WAITING
状态; - wait、notify 和 notifyAll 必须配合 Object Monitor 一起使用;而 park、unpark 不必,park没有锁的概念。
- 我们知道wait必须写在synchronized里,这是因为wait必须是由加锁的对象调用的,其原理是通过加锁的对象关联的同步监视器(Monitor)来管理线程,当调用wait方法时,把当前持锁线程放入Monitor的WaitSet,所以无论是wait还是notify都必须由被锁对象调用。
- 而park则不同,park是类方法,调用方式类似于sleep,而unpark必须指定线程对象,所以它是“精确唤醒”。
- park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,wait和notify都是以被锁对象(准确讲是其关联的Monitor)为单位的,所以 notify 只能随机唤醒一个关联的Monitor里等待的线程,notifyAll 是唤醒关联的Monitor里所有的等待的线程,就不那么【精确】 。
- park & unpark 可以先 unpark,而 wait & notify 不能先 notify。
park & unpark底层原理
设计思想
park是在怎么实现精准唤醒的哪,其实并不难理解,我们知道wait是基于锁的,所以它有竞争,所以搞了个Monitor管理被锁对象的这些个线程,但park并没有锁的概念,一个线程就一个Parker
对象,你把我park了(类似于sleep)我就暂停呗,想把我唤醒就必须精确到我现在这一个线程对象。
底层原理
在具体底层的原理上,每个线程都有自己的一个 Parker
对象,由三部分组成 _counter
, _cond
和 _mutex
。介绍一下上面第4点为啥可以先 unpark。
- case1: park -> unpark
- 当前线程调用 Unsafe.park() 方法
- 检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
- 线程进入 _cond 条件变量阻塞
- 设置 _counter = 0
- 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
- 唤醒 _cond 条件变量中的 Thread_0
- Thread_0 恢复运行
- 设置 _counter 为 0
例子:
public class TestParkUnpark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("start...");
sleep(1);
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1");
t1.start();
sleep(2);
log.debug("unpark...");
LockSupport.unpark(t1);
}
}
/*Output:
13:17:15.621 c.TestParkUnpark [t1] - start...
13:17:16.626 c.TestParkUnpark [t1] - park...
13:17:17.618 c.TestParkUnpark [main] - unpark...
13:17:17.618 c.TestParkUnpark [t1] - resume...
*/
- case2: unpark -> park
- 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为1,发现线程正在运行,则无需唤醒
- 当前线程调用 Unsafe.park() 方法
- 检查 _counter ,本情况为1,这时线程无需阻塞,继续运行
- 设置 _counter 为 0
例子:
public class TestParkUnpark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("start...");
sleep(2);
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1");
t1.start();
sleep(1);
log.debug("unpark...");
LockSupport.unpark(t1);
}
}
/*Output:
13:18:31.336 c.TestParkUnpark [t1] - start...
13:18:32.334 c.TestParkUnpark [main] - unpark...
13:18:33.344 c.TestParkUnpark [t1] - park...
13:18:33.344 c.TestParkUnpark [t1] - resume...
*/