unpark()和park()
unpark()和park()
简单总结
-
setBlocker()和setBlocker(null)总是在同一个线程内顺序执行 -
unpark()可以在任何时候调用:关键点:unpark()可以发生在park()的任意时间点- 在
park()之前:线程获得许可证,park()立即返回 - 在
park()期间:唤醒阻塞的线程 - 在
park()之后:理论上不应该(除非虚假唤醒)
- 在
-
许可证机制保证了:
- 最多只有一个许可证(0或1)
unpark()颁发许可证park()消费许可证- 顺序无关紧要
-
Blockger 的生命周期:
线程执行 park() 时: setBlocker() → [可能阻塞] → setBlocker(null) 无论是否阻塞、何时唤醒,这个顺序都固定blocker的主要作用是为了调试:- 线程被
park()挂起时,记录"为什么被挂起" - 在诊断工具(如
jstack、jvisualvm)中可以看到线程在等待什么
// 示例:使用 blocker LockSupport.park(this); // this 作为 blocker // 使用 jstack 查看时: "Thread-1" #12 prio=5 os_prio=0 tid=0x00007f8e2c1e4000 nid=0x6d03 waiting on condition [0x0000700002b3e000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b84d6d8> (a com.example.MyLock) // ^ 这里显示了 blocker 信息! - 线程被
源码:unpark()和park()
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread); // 颁发许可证(permit)
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker); // 1. 设置 blocker
UNSAFE.park(false, 0L); // 2. 关键:尝试挂起线程。无许可证(permit)阻塞挂起,有许可证(permit)直接返回
setBlocker(t, null); // 3. 清除 blocker
}
许可证(permit)的状态机:
// 每个线程有一个 permit 计数器(0 或 1)
// 初始状态:permit = 0
// unpark(thread):
// 如果 permit == 0,设为 1
// 如果 permit == 1,保持 1(不累加)
// park():
// 如果 permit == 1,消费它(设为0)并立即返回
// 如果 permit == 0,阻塞等待变成 1
完整的许可证状态机:
初始: permit=0, 无blocker
|
| park()调用
↓
setBlocker(blocker) ← 设置等待原因
|
| UNSAFE.park()
↓
permit检查:
├─ permit=1 → 消费permit(1→0) → setBlocker(null) → 立即返回
└─ permit=0 → 线程挂起
|
| unpark()或interrupt()
↓
permit可能变为1(如果是unpark)
setBlocker(null) ← 清除等待原因
源码:unpark()和park()
许可证机制设计优势:消除竞态条件
// 传统 wait/notify 的问题:
synchronized (lock) {
if (!condition) {
lock.wait(); // 如果通知发生在检查之后、wait之前?
// 就会丢失通知!
}
}
// park/unpark 没有这个问题:
if (!condition) {
LockSupport.park(); // 即使通知先到,许可证机制也能处理
}
wait()和park()对比
wait()必须在 synchronized 块内,wait() 必须配合 synchronized。
| 特性 | Object.wait() |
LockSupport.park() |
|---|---|---|
| 所属类 | Object 类的方法 |
LockSupport 工具类方法 |
| 调用要求 | 必须在 synchronized 块内 |
不需要持有任何锁 |
| 唤醒方式 | notify() / notifyAll() |
unpark(thread) |
| 中断响应 | 抛出 InterruptedException,清除中断标志 |
直接返回,不清除中断标志 |
| 许可证机制 | 无 | 有(permit 机制) |
| 虚假唤醒 | 可能发生 | 可能发生 |
浙公网安备 33010602011771号