locksupport.park()未阻塞的根本原因是线程许可为1,park()立即返回;需检查是否误调unpark(),并采用while循环校验条件+合理自旋阈值避免虚假唤醒和性能抖动。

LockSupport.park() 为什么线程没阻塞?
根本原因通常是调用 park() 前,对应线程的「许可」还剩 1 个(即之前被 unpark() 过但没消费),导致 park() 立即返回,看起来“没阻塞”。LockSupport 的许可是二进制的:最多存 1 个,多次 unpark() 和一次效果一样。
- 检查是否在
park()前误调了LockSupport.unpark(targetThread),尤其注意线程启动顺序和初始化时机 - 调试时可加日志:
Thread.currentThread().getName() + " before park, permit=" + Unsafe.getUnsafe().getAndSetInt(null, offset, 0)(不推荐生产用,仅辅助理解) - 更稳妥的做法是:在 park 前主动消耗掉残留许可——先
Thread.interrupted()(清中断状态),再用while (!shouldPark()) park()循环校验条件
实现自旋+park混合等待时,怎么避免虚假唤醒和性能抖动?
纯 park() 有调度开销,纯自旋又吃 CPU。LockSupport 本身不提供自旋逻辑,得自己组合。关键不是“要不要自旋”,而是“自旋多久才该 park”。
- 别用固定次数自旋(比如 for (int i = 0; i
- 推荐用时间阈值:自旋期间反复读取
System.nanoTime(),累计超 1000–2000 纳秒再park();JDK 9+ 可考虑Thread.onSpinWait()提示 CPU - 必须配合条件变量检查:每次
park()返回后,重新读共享状态(volatile 字段或AtomicInteger),不满足则继续循环——否则会漏掉unpark()和状态变更之间的窗口
LockSupport.unpark() 调用后线程还没 start(),许可会丢失吗?
不会丢失,但行为容易误解。许可是绑定到线程对象的,只要 Thread 实例还活着(哪怕还没调 start()),unpark() 就能成功设置许可,后续该线程第一次 park() 会直接返回。
- 典型陷阱:在新线程构造完但未
start()时就unpark(),然后主线程以为“已通知”,实际新线程可能刚进入 run() 就立即 park 返回,导致逻辑错乱 - 安全做法是:确保线程已
start()且执行到明确的等待点(比如 while 循环开头)后再unpark();可用CountDownLatch或 volatile boolean 标记“已就绪” -
unpark()对已终止线程无效,也不会报错——许可被丢弃,这点和信号量不同
和 synchronized / AQS 比,LockSupport 直接裸用的风险在哪?
它只是“线程暂停/唤醒”的原子操作,不带内存屏障语义、不保证可见性、不处理重入、不维护队列。裸用等于手动造轮子,极易出竞态。
立即学习“Java免费学习笔记(深入)”;
- 最常踩的坑:park 前读的共享变量没加
volatile或没用Unsafe.loadFence(),导致看到过期值,无限 park - 没有公平性保障——多个线程同时 park,谁先被 unpark 完全取决于 JVM 调度,无法实现 FIFO
- 调试困难:
park()不抛异常、不打印栈、jstack 里只显示java.lang.Thread.State: WAITING (parking),看不出卡在哪个逻辑分支
真正需要定制同步原语时,优先基于 AbstractQueuedSynchronizer 扩展;只有极少数场景(如协程调度器、GC safepoint 机制)才值得直接啃 LockSupport 这块硬骨头。










