死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它。
Java死锁范例
ThreadDeadlock.java
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.bjpowernode.threads;
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( 5000 );
t2.start();
Thread.sleep( 5000 );
t3.start();
}
}
class SyncThread implements Runnable{
private Object obj1;
private Object obj2;
public SyncThread(Object o1, Object o2){
this .obj1=o1;
this .obj2=o2;
}
@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();
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 + " released lock on " +obj1);
System.out.println(name + " finished execution." );
}
private void work() {
try {
Thread.sleep( 30000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
|
在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。
在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。
这些线程以向第一个对象获取*这种方式运行。但是当它试着像第二个对象获取*时,它就会进入等待状态,因为它已经被另一个线程*住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。
当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。
t1 acquiring lock on java.lang.Object@6d9dd520
t1 acquired lock on java.lang.Object@6d9dd520
t2 acquiring lock on java.lang.Object@22aed3a5
t2 acquired lock on java.lang.Object@22aed3a5
t3 acquiring lock on java.lang.Object@218c2661
t3 acquired lock on java.lang.Object@218c2661
t1 acquiring lock on java.lang.Object@22aed3a5
t2 acquiring lock on java.lang.Object@218c2661
t3 acquiring lock on java.lang.Object@6d9dd520
在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际生活所用的应用中,发现死锁并将它排除是非常难的。
分析死锁
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
2012 - 12 - 27 19 : 08 : 34
Full 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.bjpowernode.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.bjpowernode.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.bjpowernode.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: 114
Found 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.bjpowernode.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.bjpowernode.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.bjpowernode.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()方法,而且程序运行没有死锁局面,运行得很成功。
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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." );
}
|
只对有请求的进行*:你应当只想你要运行的资源获取*,比如在上述程序中我在*的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当*住那个特殊的领域而并非完全的对象。
避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。