unpark()和park()

unpark()和park()

简单总结

  1. setBlocker()setBlocker(null) 总是在同一个线程内顺序执行

  2. unpark() 可以在任何时候调用:关键点:unpark() 可以发生在 park()任意时间点

    • park() 之前:线程获得许可证,park() 立即返回
    • park() 期间:唤醒阻塞的线程
    • park() 之后:理论上不应该(除非虚假唤醒)
  3. 许可证机制保证了

    • 最多只有一个许可证(0或1)
    • unpark() 颁发许可证
    • park() 消费许可证
    • 顺序无关紧要
  4. Blockger 的生命周期

    线程执行 park() 时:
    setBlocker() → [可能阻塞] → setBlocker(null)
    无论是否阻塞、何时唤醒,这个顺序都固定
    

    blocker 的主要作用是为了调试

    • 线程被 park() 挂起时,记录"为什么被挂起"
    • 在诊断工具(如 jstackjvisualvm)中可以看到线程在等待什么
    // 示例:使用 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 机制)
虚假唤醒 可能发生 可能发生
posted @ 2025-12-08 15:18  deyang  阅读(18)  评论(0)    收藏  举报