如何检查锁定静态变量的线程?Java对象锁持有线程排查
嘿,先帮你捋捋代码里的潜在问题,再一步步说怎么排查锁的卡点:
首先贴一下你的代码(帮你补了方法返回值,不然编译不过~):
class A{ static B b; public static synchronized void init(){ b = new B(); } public static void doSomething(){ init(); synchronized(b){ // thread gets stuck after calling this } } }
先说说代码里的坑
你的init()是静态同步方法(锁的是A.class),但每次调用都会直接new一个新的B覆盖静态变量b——如果其他场景也会调用init(),很可能出现这种情况:线程A刚拿到旧的b准备加锁,线程B调用init()把b换成了新实例,这时候线程A锁的是旧实例,而其他线程可能锁的是新实例;或者旧实例的锁被某个线程一直占着,导致后续线程卡住。
一、怎么排查持有类B对象锁的线程?
Java自带的工具就能搞定,给你说几个常用的:
1. 用jstack命令(最常用的离线排查方式)
步骤超简单:
- 先找应用的PID:打开命令行输
jps,会列出所有Java进程的PID和主类名,找到你那个大型应用的ID。 - 导出线程栈:执行
jstack <PID> > thread_dump.txt,把栈信息存到文件里方便查看。 - 找锁的归属:打开文件搜你的
B类全限定名,或者找带locked <0xxxxxxx>的行——这里的0xxxxxxx是对象的内存地址。卡住的线程栈里会显示waiting to lock <0xxxxxxx>,对比地址就能找到谁拿着这个锁。
举个例子,栈里会是这样的:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f9e1802c000 nid=0x5a4 waiting for monitor entry [0x00007f9e0f7f6000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.yourpackage.A.doSomething(A.java:10)
- waiting to lock <0x000000076b6088c0> (a com.yourpackage.B)"Thread-0" #11 prio=5 os_prio=0 tid=0x00007f9e1802a000 nid=0x5a3 runnable [0x00007f9e0f8f7000]
java.lang.Thread.State: RUNNABLE
at com.yourpackage.SomeOtherClass.someMethod(SomeOtherClass.java:20)
- locked <0x000000076b6088c0> (a com.yourpackage.B)
这里Thread-0拿着<0x000000076b6088c0>这个B对象的锁,Thread-1在原地等它释放。
2. 用JConsole(图形化更直观)
不想敲命令的话用这个:
- 命令行输
jconsole,连接到你的应用进程。 - 切到【线程】标签,找状态是
BLOCKED的线程(就是卡住的那个),点它看栈信息,里面会显示它在等哪个对象的锁。 - 再翻其他线程的栈,找到带
locked关键字且对象地址匹配的线程,就是持有锁的家伙。
3. 用JVisualVM(功能更全的图形化工具)
和JConsole类似,但能做更多分析:
- 命令行输
jvisualvm,连上目标进程。 - 进【线程】面板,找到阻塞的线程,右键选【线程Dump】,然后分析dump内容,和jstack的方式一样找锁地址就行。
二、怎么检查哪个线程锁定了静态变量?
这里分两种情况:
- 如果是指
A.b这个静态变量:它本质是个对象引用,锁的是它指向的B实例,排查方式和上面完全一样,找对应的B实例地址就行。 - 如果是指类级别的锁(比如
init()方法用的A.class锁):用jstack搜locked <0xxxxxxx> (a java.lang.Class for com.yourpackage.A),就能找到持有A类锁的线程——因为静态同步方法锁的是类对象。 - 如果是其他静态对象的锁:同样找对应的对象内存地址,看哪个线程持有它的锁。
额外给个代码修复小建议
你的init()每次调用都new新的B,这大概率不是你想要的——改成懒加载单例模式吧,避免反复替换b导致锁混乱:
public static synchronized void init(){ if (b == null) { // 只有未初始化时才创建 b = new B(); } }
内容的提问来源于stack exchange,提问作者Syed Taruf Naqvi

