多线程死锁

代码

通过本类的static属性来设置锁

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
/**
* 多线程死锁的Demo
* 参考文章:
* 死锁的示例:https://blog.csdn.net/bushanyantanzhe/article/details/79398361
* synchronized关键字(深入原理,讲得很好):https://www.cnblogs.com/beiyetengqing/p/6213437.html
* 排查多线程死锁相关的工具,可以看这个文章:https://blog.csdn.net/u014039577/article/details/52351626
*/

import java.lang.Runnable;

public class DeadLock implements Runnable
{
private int value;
// 可以一行定义多个同类型的属性
// 零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
// private static Object o1 = new Object(), o2 = new Object();
// 为什么这里要把锁属性定义为static:如果不定义为static,每个new的对象,其锁是相互独立的,就无法起到真正锁的效果;即使启动多线程时传入同一个对象,也是不行的。
private static byte[] o1 = new byte[0], o2 = new byte[0];

public DeadLock(int value)
{
this.value = value;
}

public void run()
{
if (value == 1) {
synchronized(o1) {
try {
Thread.sleep(3000);
for (int i = 0; i < 10; i ++) {
System.out.println("o1->" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}

// 获取o2
synchronized(o2) {
System.out.println("o2, value = " + value);
}
}

}

if (value == 2) {
synchronized(o2) {
try {
Thread.sleep(3000);
for (int i = 10; i < 20; i ++) {
System.out.println("o2->" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}

// 获取o1
synchronized(o1) {
System.out.println("o1, value = " + value);
}
}

}

}

public static void main(String[] args)
{
DeadLock d1 = new DeadLock(1);
DeadLock d2 = new DeadLock(2);

// 启动线程是用new Thread(obj).start();
new Thread(d1).start();
new Thread(d2).start();
}
}

通过独立的类的static属性来设置锁

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
/**
* 多线程死锁的Demo1
*/

//定义两个锁
class Lock {
public static Lock locka = new Lock();
public static Lock lockb = new Lock();
}

//写一个线程
public class DeadLockThread extends Thread {
boolean flag;

DeadLockThread(boolean flag) {
this.flag = flag;
}

@Override
public void run() {
//两个线程来回切换
if (flag) {
while (true) {
//同步嵌套
//locka嵌套lockb
synchronized (Lock.locka) {
System.out.println(Thread.currentThread().getName() + "...if locka");
synchronized (Lock.lockb) {
System.out.println(Thread.currentThread().getName() + "...else lockb");
}
}
}
} else {
while (true) {
//同步嵌套
//lockb嵌套locka
synchronized (Lock.lockb) {
System.out.println(Thread.currentThread().getName() + "...if lockb");
synchronized (Lock.locka) {
System.out.println(Thread.currentThread().getName() + "...else lockb");
}
}
}
}
}

//运行后发现线程间互相抢夺资源,出现死锁的现象
public static void main(String[] args) {
DeadLockThread t1 = new DeadLockThread(false);
DeadLockThread t2 = new DeadLockThread(true);
t1.start();
t2.start();
}
}

总结

导致死锁的根源

不适当地运用“synchronized”关键词来管理线程对特定对象的访问。

“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。

排查死锁的工具

Windows下可以用可视化图形界面jconsole (Linux下也有这个命令,只是我们一般服务器不装图形界面,因此不用这个)

Linux下一般先通过jps查找到java进程ID,然后通过jstack打印栈信息,如果存在死锁,栈信息里面会注明的,类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000000054d7c618 (object 0x00000000eac13ab0, a [B),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000000054d7e9d8 (object 0x00000000eac13ac0, a [B),
which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
at DeadLock.run(DeadLock.java:60)
- waiting to lock <0x00000000eac13ab0> (a [B)
- locked <0x00000000eac13ac0> (a [B)
at java.lang.Thread.run(Unknown Source)
"Thread-0":
at DeadLock.run(DeadLock.java:40)
- waiting to lock <0x00000000eac13ac0> (a [B)
- locked <0x00000000eac13ab0> (a [B)
at java.lang.Thread.run(Unknown Source)

Found 1 deadlock.