基本使用
它们是LockSupport类中的方法
// 暂停当前线程
LockSupport.park();
// 回复某个线程的运行
LockSupport.unpark(暂停线程对象)
先park再unpark
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("start...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1");
t1.start();
Thread.sleep(2000);
log.debug("unpark...");
LockSupport.unpark(t1);
}
输出
2022/03/06-16:04:01.033 [t1] c.Test1 - start...
2022/03/06-16:04:02.048 [t1] c.Test1 - park...
2022/03/06-16:04:03.032 [main] c.Test1 - unpark...
2022/03/06-16:04:03.032 [t1] c.Test1 - resume...
先unpark在park
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("start...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("unpark...");
LockSupport.unpark(t1);
}
输出
2022/03/06-16:07:24.562 [t1] c.Test2 - start...
2022/03/06-16:07:25.569 [main] c.Test2 - unpark...
2022/03/06-16:07:26.571 [t1] c.Test2 - park...
2022/03/06-16:07:26.571 [t1] c.Test2 - resume...
特点
与Object的wait & notify相比
- wait、notify和notifyAll必须配合Object Monitor一起使用,而park,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
- 调用Unsafe.unpark(Thread-0)方法,设置_counter为1
- .唤醒_cond条件变量中的Thread-0
- Thread-0恢复运行
- 设置_counter为0
- 调用Unsafe.unpark(Thread-0)方法,设置_counter为1
- 当前线程调用Unsafe.park()方法
- 检查_counter,本情况为1,这时线程无需阻塞,继续运行
- 设置_counter为0