`
m635674608
  • 浏览: 5031372 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

如何写一个jstack检测不到的死锁

    博客分类:
  • java
 
阅读更多

通常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的锁,因此死锁就此发生。因此大家在写代码时一定要避免上面的写法,否则用常规的方法根本监测定位不到死锁。

https://my.oschina.net/robinyao/blog/807450

 
分享到:
评论

相关推荐

    操作系统死锁检测与解除

    为了检测死锁,Java提供了一个内置的诊断工具——`jstack`,它可以帮助开发者查看线程堆栈信息,找出可能存在的死锁状态。通过分析`jstack`输出,我们可以找到那些处于"等待持有锁"状态的线程,如果发现有循环等待的...

    java线程死锁实例

    2. **中断线程**:检测到死锁后,可以选择中断一个或多个线程,打破循环等待。 3. **资源分离**:将共享资源分为多个独立的部分,使得线程在请求时不会形成环路。 在描述中提到的"几个人资源共享的情况"可能指的是...

    基于Java的多线程程序死锁检查 JCarder.zip

    `JCarder` 是一个专门用于检测Java多线程程序中死锁的工具,它可以帮助开发者预防和解决这类问题。 一、Java多线程与死锁 1. **线程与进程**:在操作系统中,线程是执行单元,而进程是资源分配的基本单位。Java...

    Java 实例 - 死锁及解决方法源代码+详细指导教程.zip

    一旦检测到死锁,可以通过中断或杀死其中一个线程来解除。 7. **Java并发工具类**: Java并发库提供了多种工具类,如`ReentrantLock`、`Semaphore`和`Condition`,这些可以帮助我们更安全地管理并发,减少死锁发生...

    如何通过jstack命令dump线程信息

    这种情况下,`jstack`的输出会包含一个死锁检测段,指出哪些线程在哪个类的哪一行代码上发生了死锁。 在示例代码中,`DeadLockDemo`类的`deadLock()`方法创建了两个线程`t1`和`t2`。线程`t1`先获取锁A,然后尝试...

    什么是死锁,如何避免死锁?Java死锁详解

    6. **死锁检测与恢复**:定期检测是否存在死锁,一旦发现则终止其中一个或几个涉及死锁的进程,或者回滚事务。 理解死锁的原理,掌握避免死锁的策略,对于编写可靠的并发程序来说是至关重要的。开发者需要时刻警惕...

    【IT十八掌徐培成】Java基础第09天-01.多线程-时间片-死锁演示.zip

    Java提供了一些工具和API来检测和预防死锁,如jstack工具可以帮助开发者分析线程状态,找出可能存在的死锁问题。 在Java中,可以使用synchronized关键字来控制对共享资源的访问,以防止并发问题。synchronized可以...

    Java线程检测和数据收集工具

    例如,如果你发现一个Java应用出现阻塞或者CPU占用过高,`jstack`能提供详细的线程快照,显示每个线程的调用堆栈,从而找出问题所在。 ```bash # 使用jstack查看指定进程ID的线程堆栈信息 jstack ``` 为了定期...

    imb jca436

    2. **死锁检测**:当多个线程互相持有对方需要的资源而形成循环等待时,就会发生死锁。Jstack可以帮助我们发现并分析这些死锁情况,提供线程的持有锁信息,从而找到解决死锁的方法。 3. **锁信息**:Jstack可以展示...

    java多线程设计模式

    CyclicBarrier用于让一组线程等待其他线程到达某个点后一起继续,而CountDownLatch让一个线程等待其他线程完成操作后再继续。 以上内容仅是对Java多线程设计模式的简单概述,实际应用中还需要结合具体场景灵活运用...

    JVM调优工具命令详解.pdf

    1. jstack &lt;pid&gt;:通过指定的进程ID,jstack能够分析Java堆栈跟踪信息,并且能检测到被阻塞的线程以及它们所持有的锁。 通过JVM自带的jvisualvm工具,可以远程连接并监控JVM的实时状态,包括检测死锁。jvisualvm是...

    JVM-Tunning-Tools-Introduction

    1. jstatd:是一个监控服务,它在目标机器上创建一个RMI(远程方法调用)注册表,并使用这个注册表来监听JVM进程的创建和终止。其他工具如jvisualvm可通过它来远程连接并监控JVM进程。 2. jps:用于显示在本地或...

    多线程并发技术

    并发调试是多线程开发中不可或缺的部分,它帮助开发者检测和解决程序中的并发问题,如死锁、线程竞态等。JDK(Java开发工具包)是Java编程的核心组件,随着JDK版本的更新,它引入了更多新的并发工具和特性,这些新...

    DeadlockLabDevoxx2014

    4. **死锁检测与恢复**:Java提供了一种死锁检测机制,通过`jstack`命令可以查看线程的堆栈信息,找出可能导致死锁的线程。 5. **死锁预防**:在程序设计阶段避免可能出现死锁的场景,比如避免循环等待。 6. **死锁...

    java问题定位技术

    此外,Java提供的`jstack`命令也是一个强大的工具,它能够生成线程堆栈快照,帮助开发者分析线程的执行状态。 再者,CPU占用过高通常与无限循环、计算密集型任务或者频繁的垃圾收集有关。为了定位这类问题,我们...

    高级开发并发面试题和答案.pdf

    6. **可重入性**:`synchronized`具有可重入性,即一个线程已经获取了对象的锁,再次请求时仍然可以获得,防止死锁。 7. **ReentrantLock与synchronized**:两者都是可重入的,但ReentrantLock提供了更细粒度的控制...

    IT互联网名企经典面试题汇总:Java篇 (2).pdf

    单例模式确保一个类只有一个实例,并提供全局访问点。常见的实现方式有饿汉式、懒汉式(线程安全和非线程安全)以及双重检查锁定(DCL)等。 3. **SQL统计**: 题目要求统计每个学生分数大于80分的课程数,可以...

    java线程入门教程,涉及线程基本知识,显浅易懂..zip

    Java提供了一些工具如jstack和VisualVM来检测和分析死锁。 9. **线程局部变量(ThreadLocal)** ThreadLocal为每个线程都提供了一个独立的变量副本,确保了线程之间的数据隔离。 10. **线程安全的集合** Java...

    sun threaddump analyzer

    总的来说,Sun TDA是一个强大的故障排查工具,它极大地简化了Java应用中的线程问题诊断过程,对于Java开发者来说是不可或缺的调试利器。通过熟练使用TDA,开发者可以更快地发现并解决多线程环境下的性能瓶颈和错误,...

    Java常见面试题300

    每个桶(Bucket)实际上是一个链表,当两个键的哈希值相同时,它们会被添加到同一个桶的链表中。当查找特定键时,HashMap会先计算键的哈希值,然后遍历对应桶的链表,通过键的equals()方法来确定是否找到匹配的键值...

Global site tag (gtag.js) - Google Analytics