You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检查锁定静态变量的线程?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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 14:42:36