一般java程序排查死锁的办法是经过jstack 打印出线程信息,里面会直接显示发生死锁的状况。这里咱们先解释下查死锁能够采用的两种办法。而后咱们写一个用普通的方法检测不到的死锁。java
这里咱们先贴一个简单的发生死锁的代码。多线程
//class A public class A implements Runnable { 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 class B implements Runnable { 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 class ABTest { public static void main(String[]args){ ExecutorService executorService= Executors.newCachedThreadPool(); executorService.submit(new A()); executorService.submit(new B()); } }
##jstack查死锁办法 jstack查看死锁只须要两步:并发
这样咱们就能拿到对应进程的线程信息,好比运行上面发生死锁的代码,而后执行jstack咱们就能够看到以下信息(省落部分信息)。jvm
"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在相互等待对方锁住的锁。ide
##利用ThreadMXBean 查死锁函数
这种办法是经过JMX(Java管理扩展)提供的管理接口ThreadMXBean来查看有没有死锁发生。 不了解的JMX的基本使用的同窗能够经过下面的例子了解或者直接去搜索了解下。放个的小例子,也是从网上搜到的。this
//EchoMBean public interface EchoMBean { public String print(String name); } //Echo public class Echo implements EchoMBean { public String print(String name) { System.out.println("hi "+ name); return "hi "+name; } } // public class MbeanTest { 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 看具体的信息。.net
介绍这么多,咱们还没说怎么写个jstack检测不到的死锁。下面直接看代码,里面逻辑摘自《实战Java虚拟机》。直接先上代码:线程
public class StaticA { 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 class StaticB { 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 class StaticABTest extends Thread { private String flag; public StaticABTest(String flag) { this.flag = flag; } @Override public void run() { try { Class.forName("com.yao.bytecode.Static"+flag); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void main(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查死锁的原理差很少)。这是为什呢?code
这和JVM初始化java bean的逻辑有关系,咱们都知道JVM加载一个class 会有不少步骤:加载-> 链接(验证,准备,解析)->初始化。在初始化步骤中,JVM会执行类编译后的cinit函数,而static块里的逻辑会被编译器放到cinit函数中,当JVM执行cinit时会给cinit加上锁,防止多线程并发执行。所以当staticA staticB 进行初始化时都加上本身初始化的锁,而后在经过Class.forName去加载对方,所以都想获取对方要执行cinit的锁,所以死锁就此发生。所以你们在写代码时必定要避免上面的写法,不然用常规的方法根本监测定位不到死锁。