Java并发编程中,LockSupport是提供线程阻塞和唤醒能力的基础工具类,位于java.util.concurrent.locks包下,很多高级并发工具如ReentrantLock、Condition的实现都依赖它。其中getBlocker方法是一个容易被忽略但非常实用的工具,它可以在线程处于阻塞状态时,返回当初调用park方法时传入的阻塞对象,这个特性在排查线程阻塞问题时能发挥很大作用。
LockSupport.getBlocker方法基础介绍
LockSupport的核心方法是park和unpark,park用于阻塞当前线程,unpark用于唤醒指定线程。park方法有两个重载版本,一个是无参的,另一个是接收一个Object类型参数的,这个参数就是阻塞对象,也就是我们常说的blocker。
getBlocker方法的作用就是获取指定线程的blocker对象,它的方法签名如下:
public static Object getBlocker(Thread t)
需要注意的是,只有当线程因为调用park(Object blocker)方法而阻塞时,getBlocker才能返回对应的blocker对象;如果线程是调用无参的park方法阻塞,或者不是因为LockSupport的park操作阻塞,那么getBlocker会返回null。
getBlocker的基本使用示例
下面通过一个简单的示例演示getBlocker的使用方式:
import java.util.concurrent.locks.LockSupport;
public class LockSupportBlockerDemo {
public static void main(String[] args) throws InterruptedException {
// 定义一个阻塞对象
Object blocker = new Object();
// 创建子线程
Thread workThread = new Thread(() -> {
System.out.println("子线程开始运行,即将调用park方法阻塞");
// 传入blocker对象阻塞线程
LockSupport.park(blocker);
System.out.println("子线程被唤醒,继续执行");
});
// 启动子线程
workThread.start();
// 主线程休眠1秒,确保子线程已经进入阻塞状态
Thread.sleep(1000);
// 获取子线程的blocker对象
Object currentBlocker = LockSupport.getBlocker(workThread);
System.out.println("子线程阻塞时的blocker对象:" + currentBlocker);
System.out.println("blocker对象是否和之前定义的一致:" + (currentBlocker == blocker));
// 唤醒子线程
LockSupport.unpark(workThread);
// 再次获取blocker,此时线程已经不再阻塞,返回null
Thread.sleep(1000);
Object afterBlocker = LockSupport.getBlocker(workThread);
System.out.println("子线程被唤醒后的blocker对象:" + afterBlocker);
}
}
运行上述代码,输出结果大致如下:
子线程开始运行,即将调用park方法阻塞 子线程阻塞时的blocker对象:java.lang.Object@4eec7777 blocker对象是否和之前定义的一致:true 子线程被唤醒,继续执行 子线程被唤醒后的blocker对象:null
从结果可以看出,线程阻塞时调用getBlocker可以拿到对应的blocker对象,线程被唤醒后再次调用则返回null。
在线程转储中查看等待对象
当生产环境出现线程阻塞问题时,我们通常会通过jstack命令或者arthas等工具获取线程转储信息来分析问题。如果线程是因为调用了带blocker的park方法阻塞,线程转储中会记录对应的blocker信息,结合getBlocker方法就能快速定位阻塞关联的对象。
获取线程转储的方式
常用的获取线程转储的方式有两种:
- 使用JDK自带的jstack命令,格式为
jstack 进程ID > 转储文件路径,进程ID可以通过jps命令获取 - 使用arthas工具,启动后执行
thread -n 线程ID命令可以直接查看指定线程的详细信息
线程转储中的blocker信息解析
当线程因为LockSupport.park(blocker)阻塞时,线程转储中会显示类似如下的信息:
"work-thread" #12 prio=5 os_prio=0 tid=0x0000021b8c3a3000 nid=0x3a2c waiting on condition [0x0000002c8f7ff000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076b8c3a20> (a java.lang.Object)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at com.example.LockSupportBlockerDemo.lambda$main$0(LockSupportBlockerDemo.java:12)
at com.example.LockSupportBlockerDemo$$Lambda$1/0x0000000800060840.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
其中- parking to wait for <0x000000076b8c3a20> (a java.lang.Object)这一行就是blocker对象的信息,0x000000076b8c3a20是blocker对象的内存地址,后面的java.lang.Object是blocker对象的类型。
如果要进一步确认这个对象的具体信息,可以结合堆转储分析,或者在代码中提前给blocker对象添加标识信息,比如重写toString方法,这样线程转储中就能直接看到更有意义的标识。
getBlocker的实际应用场景
getBlocker方法在以下场景中非常实用:
- 排查并发工具导致的线程阻塞问题:比如自定义基于LockSupport的同步工具时,通过blocker标记不同的等待场景,出问题的时候可以快速区分线程是在等待哪个资源
- 线程池任务阻塞排查:如果线程池中的工作线程因为LockSupport阻塞,通过getBlocker可以快速知道任务在等待什么对象,定位资源竞争问题
- 监控线程状态:在自定义线程监控工具时,可以定时调用getBlocker获取阻塞线程的关联对象,统计不同资源的等待线程数量,提前发现资源瓶颈
注意事项
使用getBlocker方法时需要注意几个问题:
- getBlocker方法返回的是线程阻塞时的blocker对象,线程被唤醒后再次调用会返回null,所以要在确认线程处于阻塞状态时调用才有意义
- blocker对象如果已经被垃圾回收,那么getBlocker返回的对象引用可能失效,不过线程转储中记录的是对象的内存地址,只要对象还没被回收就能对应上
- 不要随意修改blocker对象的内容,因为多个线程可能共享同一个blocker对象,修改可能导致不可预期的问题
总结来说,LockSupport.getBlocker是一个辅助排查线程阻塞问题的小工具,虽然平时使用频率不高,但在遇到复杂的并发阻塞问题时,结合线程转储中的blocker信息,能大幅提升问题排查的效率。
LockSupportgetBlockerJava并发线程转储修改时间:2026-06-10 00:49:01