通常java程序排查死锁的办法是通过jstack 打印出线程信息,里面会直接显示发生死锁的情况。这里我们先解释下查死锁可以采用的两种办法。然后我们写一个用普通的方法检测不到的死锁。
这里我们先贴一个简单的发生死锁的代码。
//class A
public classAimplementsRunnable {
public void run() {
synchronized (B.class){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A.class){
System.out.println("A println: i am finished");
}
}
}
}
//class B
public classBimplementsRunnable {
public void run() {
synchronized (A.class){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B.class){
System.out.println("B println: i am finished");
}
}
}
}
//main class
public classABTest {
public static void main(String[]args){
ExecutorService executorService= Executors.newCachedThreadPool();
executorService.submit(new A());
executorService.submit(new B());
}
}
jstack查死锁办法
jstack查看死锁只需要两步:
- jps 或者 ps 拿到对应的java进程号<pid>。
- jstack <pid>
这样我们就能拿到对应进程的线程信息,比如运行上面发生死锁的代码,然后执行jstack我们就可以看到如下信息(省落部分信息)。
"pool-1-thread-2":
at com.yao.bytecode.B.run(B.java:22)
- waiting to lock <0x00000007956f8a10> (a java.lang.Class for com.yao.bytecode.B)
- locked <0x00000007956f4cb8> (a java.lang.Class for com.yao.bytecode.A)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
"pool-1-thread-1":
at com.yao.bytecode.A.run(A.java:19)
- waiting to lock <0x00000007956f4cb8> (a java.lang.Class for com.yao.bytecode.A)
- locked <0x00000007956f8a10> (a java.lang.Class for com.yao.bytecode.B)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Found one Java-level deadlock:
"pool-1-thread-2":
waiting to lock monitor 0x00007fb42c020cc8 (object 0x00000007956f8a10, a java.lang.Class),
which is held by "pool-1-thread-1"
"pool-1-thread-1":
waiting to lock monitor 0x00007fb42c0234a8 (object 0x00000007956f4cb8, a java.lang.Class),
which is held by "pool-1-thread-2"
上面的信息很清楚显示pool-1-thread-1和 pool-1-thread-2在相互等待对方锁住的锁。
利用ThreadMXBean 查死锁
这种办法是通过JMX(Java管理扩展)提供的管理接口ThreadMXBean来查看有没有死锁发生。 不了解的JMX的基本使用的同学可以通过下面的例子了解或者直接去搜索了解下。放个的小例子,也是从网上搜到的。
//EchoMBean
public interfaceEchoMBean{
public String print(String name);
}
//Echo
public classEchoimplementsEchoMBean{
public String print(String name) {
System.out.println("hi "+ name);
return "hi "+name;
}
}
//
public classMbeanTest{
public static void main(String[]args) throws Exception {
MBeanServer mBeanServer= ManagementFactory.getPlatformMBeanServer();
//这里包名要和实现类Echo的包名一致
ObjectName name=new ObjectName("com.yao.mbean:type=Echo");
Echo mbean=new Echo();
mBeanServer.registerMBean(mbean,name);
mBeanServer.invoke(name,"print",new Object[]{"hello"},new String[]{"java.lang.String"});
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
}
运行上面的代码,然后打开jconsole 或者jprofiler(需要自己安装)界面,连接到我们的上面开的程序,然后就可以通过界面操作我们注入的管理bean,输入方法参数点击执行,我们就可以在后台看到我们想要的执行。
简单介绍下Mbean的使用后我继续说下ThreadMXBean,这个接口我们主要看findMonitorDeadlockedThreads方法和查看具体线程信息方法getThreadInfo。因为jvm启动时,会自动把ThreadMXBean的实现类注入到管理平台中,因此我们可以直接通过jprofiler -> MBeans 找到java.lang Threading,然后点击operation,执行findMonitorDeadlockedThreads。即可看到结果。
然后就可以拿到发生死锁的线程id,在通过id 用getThreadInfo 看具体的信息。
jstack检测不到的死锁
介绍这么多,我们还没说怎么写个jstack检测不到的死锁。下面直接看代码,里面逻辑摘自《实战Java虚拟机》。直接先上代码:
public classStaticA{
static {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Class.forName("com.yao.bytecode.StaticB");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("StaticA init OK");
}
}
public classStaticB{
static {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Class.forName("com.yao.bytecode.StaticA");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("StaticB init OK");
}
}
public classStaticABTestextendsThread{
private String flag;
publicStaticABTest(String flag){
this.flag = flag;
}
@Override
publicvoidrun(){
try {
Class.forName("com.yao.bytecode.Static"+flag);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args)throws Exception {
StaticABTest staticA=new StaticABTest("A");
StaticABTest staticB=new StaticABTest("B");
staticA.start();
staticB.start();
staticA.join();
staticB.join();
}
}
运行上面代码,会发生死锁,程序一直运行,然后运行我们的jstack去获取thread信息,我们看不到任何死锁信息,用ThreadMXBean 也看不到(本质和jstack查死锁的原理差不多)。这是为什呢?
这和JVM初始化java bean的逻辑有关系,我们都知道JVM加载一个class 会有很多步骤:加载-> 连接(验证,准备,解析)->初始化。在初始化步骤中,JVM会执行类编译后的cinit函数,而static块里的逻辑会被编译器放到cinit函数中,当JVM执行cinit时会给cinit加上锁,防止多线程并发执行。因此当staticA staticB 进行初始化时都加上自己初始化的锁,然后在通过Class.forName去加载对方,因此都想获取对方要执行cinit的锁,因此死锁就此发生。因此大家在写代码时一定要避免上面的写法,否则用常规的方法根本监测定位不到死锁。
相关推荐
为了检测死锁,Java提供了一个内置的诊断工具——`jstack`,它可以帮助开发者查看线程堆栈信息,找出可能存在的死锁状态。通过分析`jstack`输出,我们可以找到那些处于"等待持有锁"状态的线程,如果发现有循环等待的...
2. **中断线程**:检测到死锁后,可以选择中断一个或多个线程,打破循环等待。 3. **资源分离**:将共享资源分为多个独立的部分,使得线程在请求时不会形成环路。 在描述中提到的"几个人资源共享的情况"可能指的是...
`JCarder` 是一个专门用于检测Java多线程程序中死锁的工具,它可以帮助开发者预防和解决这类问题。 一、Java多线程与死锁 1. **线程与进程**:在操作系统中,线程是执行单元,而进程是资源分配的基本单位。Java...
一旦检测到死锁,可以通过中断或杀死其中一个线程来解除。 7. **Java并发工具类**: Java并发库提供了多种工具类,如`ReentrantLock`、`Semaphore`和`Condition`,这些可以帮助我们更安全地管理并发,减少死锁发生...
这种情况下,`jstack`的输出会包含一个死锁检测段,指出哪些线程在哪个类的哪一行代码上发生了死锁。 在示例代码中,`DeadLockDemo`类的`deadLock()`方法创建了两个线程`t1`和`t2`。线程`t1`先获取锁A,然后尝试...
6. **死锁检测与恢复**:定期检测是否存在死锁,一旦发现则终止其中一个或几个涉及死锁的进程,或者回滚事务。 理解死锁的原理,掌握避免死锁的策略,对于编写可靠的并发程序来说是至关重要的。开发者需要时刻警惕...
Java提供了一些工具和API来检测和预防死锁,如jstack工具可以帮助开发者分析线程状态,找出可能存在的死锁问题。 在Java中,可以使用synchronized关键字来控制对共享资源的访问,以防止并发问题。synchronized可以...
例如,如果你发现一个Java应用出现阻塞或者CPU占用过高,`jstack`能提供详细的线程快照,显示每个线程的调用堆栈,从而找出问题所在。 ```bash # 使用jstack查看指定进程ID的线程堆栈信息 jstack ``` 为了定期...
当一个线程正在等待一个锁时,如果它被其他线程中断,应立即释放锁,以避免形成死锁。 5. 在可行的情况下,使用内置的并发工具。如java.util.concurrent包中提供的锁、信号量、条件变量等,这些工具类经常更有效地...
2. **死锁检测**:当多个线程互相持有对方需要的资源而形成循环等待时,就会发生死锁。Jstack可以帮助我们发现并分析这些死锁情况,提供线程的持有锁信息,从而找到解决死锁的方法。 3. **锁信息**:Jstack可以展示...
CyclicBarrier用于让一组线程等待其他线程到达某个点后一起继续,而CountDownLatch让一个线程等待其他线程完成操作后再继续。 以上内容仅是对Java多线程设计模式的简单概述,实际应用中还需要结合具体场景灵活运用...
1. jstack <pid>:通过指定的进程ID,jstack能够分析Java堆栈跟踪信息,并且能检测到被阻塞的线程以及它们所持有的锁。 通过JVM自带的jvisualvm工具,可以远程连接并监控JVM的实时状态,包括检测死锁。jvisualvm是...
1. jstatd:是一个监控服务,它在目标机器上创建一个RMI(远程方法调用)注册表,并使用这个注册表来监听JVM进程的创建和终止。其他工具如jvisualvm可通过它来远程连接并监控JVM进程。 2. jps:用于显示在本地或...
并发调试是多线程开发中不可或缺的部分,它帮助开发者检测和解决程序中的并发问题,如死锁、线程竞态等。JDK(Java开发工具包)是Java编程的核心组件,随着JDK版本的更新,它引入了更多新的并发工具和特性,这些新...
4. **死锁检测与恢复**:Java提供了一种死锁检测机制,通过`jstack`命令可以查看线程的堆栈信息,找出可能导致死锁的线程。 5. **死锁预防**:在程序设计阶段避免可能出现死锁的场景,比如避免循环等待。 6. **死锁...
此外,Java提供的`jstack`命令也是一个强大的工具,它能够生成线程堆栈快照,帮助开发者分析线程的执行状态。 再者,CPU占用过高通常与无限循环、计算密集型任务或者频繁的垃圾收集有关。为了定位这类问题,我们...
6. **可重入性**:`synchronized`具有可重入性,即一个线程已经获取了对象的锁,再次请求时仍然可以获得,防止死锁。 7. **ReentrantLock与synchronized**:两者都是可重入的,但ReentrantLock提供了更细粒度的控制...
单例模式确保一个类只有一个实例,并提供全局访问点。常见的实现方式有饿汉式、懒汉式(线程安全和非线程安全)以及双重检查锁定(DCL)等。 3. **SQL统计**: 题目要求统计每个学生分数大于80分的课程数,可以...
Java提供了一些工具如jstack和VisualVM来检测和分析死锁。 9. **线程局部变量(ThreadLocal)** ThreadLocal为每个线程都提供了一个独立的变量副本,确保了线程之间的数据隔离。 10. **线程安全的集合** Java...
总的来说,Sun TDA是一个强大的故障排查工具,它极大地简化了Java应用中的线程问题诊断过程,对于Java开发者来说是不可或缺的调试利器。通过熟练使用TDA,开发者可以更快地发现并解决多线程环境下的性能瓶颈和错误,...