死锁是开发中必须要避免的问题,它会导致线程运行不下去,功能出现问题。下面介绍四种定位死锁的方法。
首先,先来看一个简单的死锁例子
public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { DealThread t1 = new DealThread(); t1.setFlag("a"); Thread thread1 = new Thread(t1); thread1.start(); Thread.sleep(1000); t1.setFlag("b"); Thread thread2 = new Thread(t1); thread2.start(); } static class DealThread implements Runnable{ public String username; public Object lock1 = new Object(); public Object lock2 = new Object(); public void setFlag(String username) { this.username = username; } @Override public void run() { if("a".equals(username)) { synchronized (lock1) { try { System.out.println("username= " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock2) { System.out.println("按lock1->lock2代码顺序执行了"); } } } if("b".equals(username)) { synchronized (lock2) { try { System.out.println("username= " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock1) { System.out.println("按lock2->lock1代码顺序执行了"); } } } } } }JStack是JDK自带的命令行工具,主要用于线程Dump分析。Dump文件是线程的内存镜像,保存的是进程的执行状态信息。它的位置在JAVA_HOME/bin目录下。它不是图形界面,所以不能双击打开。 先打开CMD命令行,执行jps命令,查看Java进程信息,找出要调试的Java进程号,也就是pid,我这里是12980 执行jstack -l pid,-l参数可以打印出锁的相关信息,如果是真实项目可能会打印出很多信息,我们可以使用 jstack -l pid > D:deal_thread.log命令将所有信息打印到deal_thread.log文件中。 打印结果较多,只截出死锁的相关信息
Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x000000000363d9d8 (object 0x00000000d5cf9808, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x000000000363d878 (object 0x00000000d5cf9818, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at com.morlia.platform.synchronizedtest.SynchronizedTest$DealThread.run(SynchronizedTest.java:69) - waiting to lock <0x00000000d5cf9808> (a java.lang.Object) - locked <0x00000000d5cf9818> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) "Thread-0": at com.morlia.platform.synchronizedtest.SynchronizedTest$DealThread.run(SynchronizedTest.java:55) - waiting to lock <0x00000000d5cf9818> (a java.lang.Object) - locked <0x00000000d5cf9808> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) Found 1 deadlock.此时从打印信息可以看出Thread-0和Thread-1线程在run方法中死锁了。
JConsole是JDK自带的一个虚拟机监控工具,它的位置在JAVA_HOME/bin目录下,双击打开之后,选择可能死锁的线程,点击连接。 进入监控页面之后先点击线程,然后点击左下角的检测死锁 如果发生死锁,在死锁一栏就会出现死锁的线程信息 此信息和jstack打印出的信息类似
JVisualVM是jdk提供的一个非常强大的排查Java程序问题的工具,可以监控程序性能、查看jvm配置信息、堆快照、线程堆栈信息。算是程序优化的必备工具。工具位于JDK的bin目录中,也是图形界面,可以直接双击打开。 左边选择你需要看的进程,在右边先点击进程页签,然后点击线程页签,此时我们可以看到有红字提示说检测到死锁,于是,我们点击线程Dump来获取更多信息。 此时它会出现threaddump页面,上面的信息就是dump文件的信息。
有时候,我们希望程序能自己发现死锁,而不是需要我们来寻找。Java就提供了这样的工具类。 样例代码:
public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { DealThread t1 = new DealThread(); t1.setFlag("a"); Thread thread1 = new Thread(t1); thread1.start(); Thread.sleep(1000); t1.setFlag("b"); Thread thread2 = new Thread(t1); thread2.start(); Thread.sleep(4000); //获取xbean实例 ThreadMXBean mBean = ManagementFactory.getThreadMXBean(); //获取死锁的线程ID long[] dealThreads = mBean.findDeadlockedThreads(); //遍历 for(long pid : dealThreads) { //获取线程信息 ThreadInfo threadInfo = mBean.getThreadInfo(pid); System.out.println(threadInfo); } } static class DealThread implements Runnable{ public String username; public Object lock1 = new Object(); public Object lock2 = new Object(); public void setFlag(String username) { this.username = username; } @Override public void run() { if("a".equals(username)) { synchronized (lock1) { try { System.out.println("username= " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock2) { System.out.println("按lock1->lock2代码顺序执行了"); } } } if("b".equals(username)) { synchronized (lock2) { try { System.out.println("username= " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock1) { System.out.println("按lock2->lock1代码顺序执行了"); } } } } } }运行结果
username= a username= b "Thread-1" Id=11 BLOCKED on java.lang.Object@15db9742 owned by "Thread-0" Id=10 "Thread-0" Id=10 BLOCKED on java.lang.Object@6d06d69c owned by "Thread-1" Id=11检测死锁和获取堆栈信息是比较耗费性能的操作,不能频繁去使用。 死锁是程序设计的Bug,在设计程序时要避免双方互相持有对方的锁,只要互相等待对方释放锁,就有可能出现死锁。
