关于死锁产生条件以及死锁示例

死锁产生条件

死锁产生有四大必要条件,只要不满足其中一种即可避免死锁

  • 互斥条件: 资源是独占的,一次只能被一个线程使用。
  • 请求与保持条件: 线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源被其他线程占有。
  • 不剥夺条件: 线程已获得的资源在未使用完之前,不能被强行剥夺。
  • 循环等待条件: 若干线程之间形成一种头尾相接的循环等待资源关系。(例如线程A持有资源1,需要资源2;线程B持有资源2,需要资源3;线程C持有资源3,需要资源1)

几种常见死锁示例

嵌套锁死锁

嵌套锁死锁在一个线程试图以不同顺序获取多个锁可能发生,例如:

  • 线程A获取锁1,然后尝试获取锁2
  • 线程B获取锁2,然后尝试获取锁1

示例代码

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
public class NestedLockDeadlock {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();

public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: 持有 lock1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: 等待 lock2");
synchronized (lock2) {
System.out.println("Thread 1: 持有 lock1 和 lock2");
}
}
});

Thread thread2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: 持有 lock2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: 等待 lock1");
synchronized (lock1) {
System.out.println("Thread 2: 持有 lock2 和 lock1");
}
}
});

thread1.start();
thread2.start();
}
}

资源锁死锁

最常见的死锁,在线程竞争有限资源时会发生

示例代码:

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
public class ResourceDeadlock {
public static void main(String[] args) {
// 创建两个资源对象
Object resource1 = new Object();
Object resource2 = new Object();

// 创建第一个线程
Thread t1 = new Thread(() -> {
// 线程1首先尝试获取resource1
synchronized (resource1) {
System.out.println("线程1获取到resource1");
try {
// 线程1睡眠500毫秒,模拟持有resource1一段时间
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("线程等待异常");
}
// 线程1在持有resource1的同时,尝试获取resource2
synchronized (resource2) {
System.out.println("线程1获取到resource2");
}
}
});

// 创建第二个线程
Thread t2 = new Thread(() -> {
// 线程2首先尝试获取resource2
synchronized (resource2) {
System.out.println("线程2获取到resource2");
try {
// 线程2睡眠500毫秒,模拟持有resource2一段时间
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("线程等待异常");
}
// 线程2在持有resource2的同时,尝试获取resource1
synchronized (resource1) {
System.out.println("线程2获取到resource1");
}
}
});

// 启动两个线程
t1.start();
t2.start();
}
}

哲学家就餐问题

多个哲学家(线程)共享有限的餐具(资源),可能导致所有哲学家都在等待其他人放下餐具的情况。哲学家就餐问题可以视为已从特殊的资源锁死锁。

示例代码:

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
import java.util.*;

// Main类,程序的入口点
public class Main {
public static void main(String[] args) throws InterruptedException{
// 创建4个哲学家对象
Philosopher philosopher1 = new Philosopher(0);
Philosopher philosopher2 = new Philosopher(1);
Philosopher philosopher3 = new Philosopher(2);
Philosopher philosopher4 = new Philosopher(3);

// 创建一个有4把筷子的桌子
Table table = new Table(4);

// 让每个哲学家开始他们的行为
philosopher1.repeat(table);
philosopher2.repeat(table);
philosopher3.repeat(table);
philosopher4.repeat(table);
}
}

// 桌子类,代表筷子的集合
class Table {
ArrayList<Object> chopsticks = new ArrayList<>();

// 构造函数,初始化n把筷子
public Table(int n) {
for (int i = 0; i < n; i++) {
this.chopsticks.add(new Object());
}
}
}

// 哲学家类
class Philosopher {
int num; // 哲学家的编号

// 哲学家的行为方法
public void repeat(Table table){
Thread t1 = new Thread() {
@Override
public void run(){
// 尝试拿起左手边的筷子(根据哲学家编号)
synchronized (table.chopsticks.get(num)) {
System.out.println("哲学家"+num+"拿起左手筷子");
try {
Thread.sleep(3000); // 等待3秒,增加死锁可能性
}catch (InterruptedException e) {
System.out.println("线程等待异常");
return;
}

// 尝试拿起右手边的筷子
// 注意:最后一个哲学家的右手筷子是第一个筷子
if (num == table.chopsticks.size() - 1) {
synchronized (table.chopsticks.get(0)) {
System.out.println("哲学家"+num+"拿起右手筷子");
}
}else {
synchronized (table.chopsticks.get(num + 1)) {
System.out.println("哲学家"+num+"拿起右手筷子");
}
}
}
}
};
t1.start(); // 启动线程
}

// 构造函数,设置哲学家编号
public Philosopher(int n) {
this.num = n;
}
}