Java死锁范例以及如何分析死锁
来源:互联网 发布:达芬奇调色软件语言 编辑:程序博客网 时间:2024/04/29 13:07
转载说明:
本文由 ImportNew - 范琦琦 翻译自 journaldev。欢迎加入翻译小组。转载请见文末要求。
死锁是两个甚至多个线程,由于彼此等待获取对方资源,被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它在运行时将会引起死锁,然后我们就会明白如何分析它。
Java死锁范例
ThreadDeadlock.java
public class ThreadDeadlock { public static void main(String[] args) throws InterruptedException { Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1"); Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2"); Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3"); t1.start(); Thread.sleep(5*1000); t2.start(); Thread.sleep(5*1000); t3.start(); }}class SyncThread implements Runnable { private Object obj1; private Object obj2; public SyncThread(Object obj1, Object obj2) { this.obj1 = obj1; this.obj2 = obj2; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " acquiring lock on " + obj1); synchronized (obj1){ System.out.println(name + " acquired lock on " + obj1); work(name); System.out.println(name + " attempt to acquire lock on " + obj2); synchronized (obj2){ System.out.println(name + " acquired lock on " + obj2); work(name); } System.out.println(name + " release lock on " + obj2); } System.out.println(name + " release lock on " + obj1); System.out.println(name + " finished execution"); } private void work(String name) { try { Thread.sleep(10*1000); System.out.println(name + " is working ..."); } catch (InterruptedException e) { e.printStackTrace(); } }}
在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。
在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。
这些线程以向第一个对象获取封锁这种方式运行。但是当它试着像第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。
当我执行上面的程序时,就产生如下了输出,但是程序却因为死锁无法停止。
t1 acquiring lock on java.lang.Object@6b8cebb0t1 acquired lock on java.lang.Object@6b8cebb0t2 acquiring lock on java.lang.Object@441ded1et2 acquired lock on java.lang.Object@441ded1et1 is working ...t1 attempt to acquire lock on java.lang.Object@441ded1et3 acquiring lock on java.lang.Object@724d9353t3 acquired lock on java.lang.Object@724d9353t2 is working ...t2 attempt to acquire lock on java.lang.Object@724d9353t3 is working ...t3 attempt to acquire lock on java.lang.Object@6b8cebb0
在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际生活所用的应用中,发现死锁并将它排除是非常难的。
分析死锁
为了分析一个死锁,我们需要关注下应用中的Java线程转存,例如可以使用VisualVM收集资料,或者jstack应用程序产生线程转存。
以下就是上述程序的线程转存内容。
2012-12-27 19:08:34Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode):"Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f658> (a java.lang.Object) - locked <0x000000013df2f678> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)"t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f678> (a java.lang.Object) - locked <0x000000013df2f668> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)"t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f668> (a java.lang.Object) - locked <0x000000013df2f658> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)"Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE"C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE"Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)"Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:503) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) - locked <0x000000013de75320> (a java.lang.ref.Reference$Lock)"VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable "GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable "GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable "GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable "GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable "GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable "GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable "GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable "GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable "VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition JNI global references: 114Found one Java-level deadlock:============================="t3": waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object), which is held by "t1""t1": waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object), which is held by "t2""t2": waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object), which is held by "t3"Java stack information for the threads listed above:==================================================="t3": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f658> (a java.lang.Object) - locked <0x000000013df2f678> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)"t1": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f668> (a java.lang.Object) - locked <0x000000013df2f658> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)"t2": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f678> (a java.lang.Object) - locked <0x000000013df2f668> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722)Found 1 deadlock.
这三个线程转存的输出清楚地说明了死锁环境和线程,以及包含死锁环境的资源。
为了分析死锁,我们需要关注死锁状态的线程,然后资源再等待去封锁,每一个资源都有一个独特的ID,有了这个ID我们就能发现是哪一个进程已经封锁住对象。举个例子,线程“t3”正在等待封锁0x000000013df2f658,但是它已经被线程“t1”封锁住了。
当我们分析死锁环境的时候,如果发现线程正在引起死锁,这是我们就要改变代码来避免死锁的产生。
避免死锁
有很多方针可供我们使用来避免死锁的局面。
①。 避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。
public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " acquiring lock on " + obj1); synchronized (obj1) { System.out.println(name + " acquired lock on " + obj1); work(); } System.out.println(name + " released lock on " + obj1); System.out.println(name + " acquiring lock on " + obj2); synchronized (obj2) { System.out.println(name + " acquired lock on " + obj2); work(); } System.out.println(name + " released lock on " + obj2); System.out.println(name + " finished execution.");}
②。只对有请求的资源进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。
③。避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。
原文链接: journaldev 翻译: ImportNew.com - 范琦琦
译文链接: http://www.importnew.com/9668.html
[ 转载请保留原文出处、译者和译文链接。]
死锁检测方法补充:
除了上述使用线程转储文件分析死锁外,jdk自带了GUI可视化死锁检测工具jconsole(内置了内存/线程/类/VM概况/MBeans等项目的分析)
以Linux系统上的使用为例,具体操作步骤为:
①。Terminal中打开jconsole,启动jconsole:
②。选择程序所在的java进程:
③。点击“Detect Deadlock”按钮:
④。选择jconsole检测出来的具体死锁线程,定位死锁位置:
标红处提示我们,线程t2请求公共资源,而此时却被线程t3持有,并被阻塞住。通过查看线程t3,t1,可以发现t1被t2阻塞,t2被t3阻塞,t3被t1阻塞,任一线程均无法释放自己,故形成死锁。
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例以及如何分析死锁
- Java死锁范例与死锁分析
- 如何分析Java虚拟机死锁
- 如何分析Java虚拟机死锁
- 死锁以及如何避免死锁
- 分析Java 死锁以及MemoryLeak问题
- 分析Java 死锁以及MemoryLeak问题
- 死锁的产生以及如何避免死锁
- Java死锁以及查看
- 什么是死锁?如何分析和避免死锁?
- JAVA多线程死锁分析
- Java死锁问题分析
- 常见的求最大公约数和最小公倍数的算法
- 阶乘最右边非0数
- python核心编程--第十一章
- jQuery.i18n.properties 中文说明文档
- 分享一个快速加载dex文件的方法
- Java死锁范例以及如何分析死锁
- Git 常用命令大全
- mybatis 中 foreach collection的三种用法
- 支付宝开发平台之第三方授权登录与获取用户信息
- 学好Windows编程要看的书籍
- 软件反破解的几个思路
- Shell中$# $@等含义
- 第一次面试
- VB编程:获取文件夹下所有文件-1