LockSupport.park的blocker参数用于在线程dump时记录阻塞原因,JVM将其toString()结果写入WAITING状态的堆栈信息,便于jstack等工具定位死锁或长阻塞问题;它仅在park期间有效,需传入重写toString()的有意义对象(如含锁类型、资源ID),不可为null或无意义对象。

LockSupport.park 的 blocker 参数到底有什么用
它不是装饰品,而是 JVM 线程 dump 时唯一能被 java.lang.Thread.getStackTrace() 之外直接暴露的自定义上下文。当线程调用 LockSupport.park(blocker) 挂起时,JVM 会把 blocker 对象的 toString() 结果记录进 Thread.State = WAITING 的堆栈帧里——这意味着 jstack 或 JFR、Arthas 的线程快照里能看到它。
但注意:这个字段只在 park 期间有效;一旦线程被 unpark 或中断,blocker 就会被清空(JDK 9+ 更严格,甚至不保留引用)。所以它不能替代日志,只是“快照瞬间”的上下文快照。
怎么构造一个有意义的 blocker 对象
别传 null,也别传随便一个 new Object()——那样 jstack 里只会显示 java.lang.Object@1a2b3c,毫无价值。你需要一个重写了 toString() 的对象,内容要包含:锁类型、资源标识、调用位置。
- 用匿名内部类或 record(JDK 14+)快速构建,避免额外类污染代码库
-
toString()返回字符串必须简短(建议 ≤ 80 字符),过长会被截断或影响 jstack 可读性 - 不要在
toString()里触发 I/O、同步块或复杂计算,否则 park 本身可能卡住
示例:
LockSupport.park(new Object() {
public String toString() {
return "ReentrantLock@0x" + Integer.toHexString(System.identityHashCode(myLock))
+ "[held by " + ownerThreadName + "]";
}
});哪些地方适合加 blocker(而不是 everywhere)
盲目给每个 park() 都加 blocker 会造成冗余和干扰。真正需要的是那些「可能长期挂起」且「缺乏外部可观测信号」的点:
- 自定义 AQS 同步器的
tryAcquire()失败后 park 前(比如AbstractQueuedSynchronizer.doAcquireInterruptibly的等效逻辑) - 无界阻塞队列(如
LinkedBlockingQueue的take())底层 park 调用处(需 patch 或代理) - 手动实现的条件等待(非
Condition.await(),而是基于 park/unpark 的轮子)
别在 Thread.sleep()、Object.wait() 或标准 ReentrantLock.lock() 上强行 hack blocker——它们不接受该参数,强行绕过会破坏语义。
排查时怎么看 blocker 信息
运行 jstack <pid></pid>,找到类似这样的线程状态行:
"pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x00007f8a3c00a800 nid=0x3e03 waiting on condition [0x00007f8a2d7f9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at com.example.MySync.waitOn(MySync.java:42)
-< blocked on <0x000000071a2b3c40> (a java.lang.Object) <-- 注意这里!
-< blocker is <0x000000071a2b3c40> (a com.example.MySync$1) <-- blocker 引用地址
-< blocker.toString() = "Mutex@0xabc123[waiting for resource 'db-conn-5']"关键看最后那行 blocker.toString() 输出。如果没看到这行,说明 blocker 是 null 或未正确设置;如果看到但内容为空或混乱,检查 toString() 是否抛异常(会被静默吞掉)或返回 null。
真实死锁场景中,多个线程的 blocker 字符串往往指向同一资源 ID 或循环依赖链(如 A 等 B 的 “lock-x”,B 等 A 的 “lock-y”),这时比光看 stack trace 更容易定位瓶颈点。
blocker 不是银弹:它只在 park 瞬间有效,且完全依赖开发者主动注入。漏掉一个关键 park 点,就可能让整个死锁链缺失一环。

















