Java死锁_动力节点Java学院整理
死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它。
Java死锁范例
ThreadDeadlock.java
packagecom.bjpowernode.threads; publicclassThreadDeadlock{ publicstaticvoidmain(String[]args)throwsInterruptedException{ Objectobj1=newObject(); Objectobj2=newObject(); Objectobj3=newObject(); Threadt1=newThread(newSyncThread(obj1,obj2),"t1"); Threadt2=newThread(newSyncThread(obj2,obj3),"t2"); Threadt3=newThread(newSyncThread(obj3,obj1),"t3"); t1.start(); Thread.sleep(5000); t2.start(); Thread.sleep(5000); t3.start(); } } classSyncThreadimplementsRunnable{ privateObjectobj1; privateObjectobj2; publicSyncThread(Objecto1,Objecto2){ this.obj1=o1; this.obj2=o2; } @Override publicvoidrun(){ Stringname=Thread.currentThread().getName(); System.out.println(name+"acquiringlockon"+obj1); synchronized(obj1){ System.out.println(name+"acquiredlockon"+obj1); work(); System.out.println(name+"acquiringlockon"+obj2); synchronized(obj2){ System.out.println(name+"acquiredlockon"+obj2); work(); } System.out.println(name+"releasedlockon"+obj2); } System.out.println(name+"releasedlockon"+obj1); System.out.println(name+"finishedexecution."); } privatevoidwork(){ try{ Thread.sleep(30000); }catch(InterruptedExceptione){ e.printStackTrace(); } } }
在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。
在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。
这些线程以向第一个对象获取封锁这种方式运行。但是当它试着像第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。
当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。
t1acquiringlockonjava.lang.Object@6d9dd520
t1acquiredlockonjava.lang.Object@6d9dd520
t2acquiringlockonjava.lang.Object@22aed3a5
t2acquiredlockonjava.lang.Object@22aed3a5
t3acquiringlockonjava.lang.Object@218c2661
t3acquiredlockonjava.lang.Object@218c2661
t1acquiringlockonjava.lang.Object@22aed3a5
t2acquiringlockonjava.lang.Object@218c2661
t3acquiringlockonjava.lang.Object@6d9dd520
在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际生活所用的应用中,发现死锁并将它排除是非常难的。
分析死锁
2012-12-2719:08:34 FullthreaddumpJavaHotSpot(TM)64-BitServerVM(23.5-b02mixedmode): "AttachListener"daemonprio=5tid=0x00007fb0a2814000nid=0x4007waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE "DestroyJavaVM"prio=5tid=0x00007fb0a2801000nid=0x1703waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE "t3"prio=5tid=0x00007fb0a204b000nid=0x4d07waitingformonitorentry[0x000000015d971000] java.lang.Thread.State:BLOCKED(onobjectmonitor) atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f658>(ajava.lang.Object) -locked<0x000000013df2f678>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) "t2"prio=5tid=0x00007fb0a1073000nid=0x4207waitingformonitorentry[0x000000015d209000] java.lang.Thread.State:BLOCKED(onobjectmonitor) atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f678>(ajava.lang.Object) -locked<0x000000013df2f668>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) "t1"prio=5tid=0x00007fb0a1072000nid=0x5503waitingformonitorentry[0x000000015d86e000] java.lang.Thread.State:BLOCKED(onobjectmonitor) atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f668>(ajava.lang.Object) -locked<0x000000013df2f658>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) "ServiceThread"daemonprio=5tid=0x00007fb0a1038000nid=0x5303runnable[0x0000000000000000] java.lang.Thread.State:RUNNABLE "C2CompilerThread1"daemonprio=5tid=0x00007fb0a1037000nid=0x5203waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE "C2CompilerThread0"daemonprio=5tid=0x00007fb0a1016000nid=0x5103waitingoncondition[0x0000000000000000] java.lang.Thread.State:RUNNABLE "SignalDispatcher"daemonprio=5tid=0x00007fb0a4003000nid=0x5003runnable[0x0000000000000000] java.lang.Thread.State:RUNNABLE "Finalizer"daemonprio=5tid=0x00007fb0a4800000nid=0x3f03inObject.wait()[0x000000015d0c0000] java.lang.Thread.State:WAITING(onobjectmonitor) atjava.lang.Object.wait(NativeMethod) -waitingon<0x000000013de75798>(ajava.lang.ref.ReferenceQueue$Lock) atjava.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) -locked<0x000000013de75798>(ajava.lang.ref.ReferenceQueue$Lock) atjava.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) atjava.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177) "ReferenceHandler"daemonprio=5tid=0x00007fb0a4002000nid=0x3e03inObject.wait()[0x000000015cfbd000] java.lang.Thread.State:WAITING(onobjectmonitor) atjava.lang.Object.wait(NativeMethod) -waitingon<0x000000013de75320>(ajava.lang.ref.Reference$Lock) atjava.lang.Object.wait(Object.java:503) atjava.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) -locked<0x000000013de75320>(ajava.lang.ref.Reference$Lock) "VMThread"prio=5tid=0x00007fb0a2049800nid=0x3d03runnable "GCtaskthread#0(ParallelGC)"prio=5tid=0x00007fb0a300d800nid=0x3503runnable "GCtaskthread#1(ParallelGC)"prio=5tid=0x00007fb0a2001800nid=0x3603runnable "GCtaskthread#2(ParallelGC)"prio=5tid=0x00007fb0a2003800nid=0x3703runnable "GCtaskthread#3(ParallelGC)"prio=5tid=0x00007fb0a2004000nid=0x3803runnable "GCtaskthread#4(ParallelGC)"prio=5tid=0x00007fb0a2005000nid=0x3903runnable "GCtaskthread#5(ParallelGC)"prio=5tid=0x00007fb0a2005800nid=0x3a03runnable "GCtaskthread#6(ParallelGC)"prio=5tid=0x00007fb0a2006000nid=0x3b03runnable "GCtaskthread#7(ParallelGC)"prio=5tid=0x00007fb0a2006800nid=0x3c03runnable "VMPeriodicTaskThread"prio=5tid=0x00007fb0a1015000nid=0x5403waitingoncondition JNIglobalreferences:114 FoundoneJava-leveldeadlock: ============================= "t3": waitingtolockmonitor0x00007fb0a1074b08(object0x000000013df2f658,ajava.lang.Object), whichisheldby"t1" "t1": waitingtolockmonitor0x00007fb0a1010f08(object0x000000013df2f668,ajava.lang.Object), whichisheldby"t2" "t2": waitingtolockmonitor0x00007fb0a1012360(object0x000000013df2f678,ajava.lang.Object), whichisheldby"t3" Javastackinformationforthethreadslistedabove: =================================================== "t3": atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f658>(ajava.lang.Object) -locked<0x000000013df2f678>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) "t1": atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f668>(ajava.lang.Object) -locked<0x000000013df2f658>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) "t2": atcom.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41) -waitingtolock<0x000000013df2f678>(ajava.lang.Object) -locked<0x000000013df2f668>(ajava.lang.Object) atjava.lang.Thread.run(Thread.java:722) Found1deadlock.
这三个线程转存的输出清楚地说明了死锁环境和线程,以及包含死锁环境的资源。
为了分析死锁,我们需要关注死锁状态的线程,然后资源再等待去封锁,每一个资源都有一个独特的ID,有了这个ID我们就能发现是哪一个进程已经封锁住对象。举个例子,线程“t3”正在等待封锁0x000000013df2f658,但是它已经被线程“t1”封锁住了。
当我们分析死锁环境的时候,如果发现线程正在引起死锁,这是我们就要改变代码来避免死锁的产生。
避免死锁
有很多方针可供我们使用来避免死锁的局面。
避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。
publicvoidrun(){ Stringname=Thread.currentThread().getName(); System.out.println(name+"acquiringlockon"+obj1); synchronized(obj1){ System.out.println(name+"acquiredlockon"+obj1); work(); } System.out.println(name+"releasedlockon"+obj1); System.out.println(name+"acquiringlockon"+obj2); synchronized(obj2){ System.out.println(name+"acquiredlockon"+obj2); work(); } System.out.println(name+"releasedlockon"+obj2); System.out.println(name+"finishedexecution."); }
只对有请求的进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。
避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。