文章目录
- 1. 简介
- 2. 底层原理
- 3. 总结
1. 简介
park和unpark也是一个线程暂停技术(让线程进入wait状态),与wait/notify不同的是,前者是LockSupport类中的方法,后者是Object类中的方法。其次,wait与notify和notifyall必须配合Object Monitor一起使用,而park和unpark不必一起使用。park和unpark是以线程为单位来阻塞和唤醒线程的,而notify只能随机唤醒一个等待线程。最后park和unpark可以先unpark,而wait和notify不行。
2. 底层原理
每个线程都有自己的一个Parker对象(c代码实现),由三个部分组成_counter,_cond, _mutex,打个比喻:
- 线程就像一个旅人,Parker就是其携带的背包,条件变量(_cond)就好比背包中的帐篷,_couter就好比备用的干粮
- 调用park就是看需不需要停下来休息,如果干粮没有了就进帐篷休息,如果还有干粮就继续前进
- 调用unpark就好比令干粮充足,如果现在线程还在帐篷中,就唤醒他继续前进,如果线程还在运行,那么下次它调用park,仅消耗掉备用干粮,继续前进,因为背包容量有限,所以多次调用unpark只会补充一份干粮。
先调用park,再调用unpark:
- 当前线程调用unsafe.park()方法
- 检查_counter,本情况为0,这是,获得_mutex互斥锁
- 线程进入cond条件变量阻塞
- 设置_counter=0
- 调用Unsafe.unpark(Thread 0)方法,设置counter为1
- 唤醒_cond条件变量中的Thread_0
- Thread 0恢复运行
- 设置_countre为0
先调用unpark再调用park:
- 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
- 当前线程调用Unsafe.park()方法
- 检查_counter,本情况为1,这时现场无需阻塞,继续运行
- 设置_counter为0
3. 总结
先调用 park 再调用 unpark:如果先调用 park,那么当前线程将进入阻塞状态,等待被其他线程调用 unpark 方法唤醒。
先调用 unpark 再调用 park:如果先调用 unpark,它将为当前线程创建一个"许可证",以便稍后可以使用。这个许可证不会累积,每个线程最多只能有一个许可证。当稍后调用 park 时,如果已经有一个 “许可证”(通过先调用 unpark 而获得),那么 park 不会阻塞,它会消耗掉许可证,线程继续执行。如果没有可用的许可证,park 将阻塞。
所以,先调用 unpark 再调用 park 通常用于确保某个操作发生之前,线程不会被阻塞,而先调用 park 再调用 unpark 则用于将线程置于阻塞状态,等待特定条件的发生。需要注意的是,unpark 方法可以多次调用,但它只会为一个线程创建一个许可证。如果多次调用 unpark,线程仍然只会有一个许可证,而不会累积多个许可证。这意味着多次调用 unpark 不会使 park 方法重复消耗多个许可证。