1、什么是死锁?
死锁是一种非常严重的bug,是说多个线程同时被阻塞,线程中的一个或者多个又或者全部都在等待某个资源被释放,造成线程无限期的阻塞,导致程序不能正常终止
为了进一步说明死锁,有哲学家就餐这样的一个问题:
有一个桌子,哲学家们围成一圈,每两个哲学家中间有一支筷子
哲学家只能两件事:思考或者吃饭,思考时候就不会动筷子,吃饭时会拿起左右手旁边的筷子(先拿左后拿右)
2、死锁产生原因?
结合上述哲学家的例子,说明死锁产生的四个必要条件:
互斥使用:当资源被一个线程使用或者占用时,别的线程不能使用该资源
不可抢占:获取资源的一方,不能从正在使用资源的一方抢占掠夺资源,资源只能被使用者主动释放
请求和保持:资源请求者在请求别的资源时,同时保持对已有资源的占有
循环等待:即p1占有p2的资源,p2占有p3的资源,p3占有p1的资源,这样形成了一个等待环路
上述这四个条件满足即造成的结果就是死锁
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
|
public class DeadLock {
publi static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread() {
@Override
public void run() {
try {
synchronized (lock1) {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("输出t1");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
synchronized (lock1) {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("输出t2");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
t2.start();
}
}
|
说明:
t1先申请lock1,lock2,释放lock2,lock1
t2后申请lock2,lock1,释放lock1,lock2
这种情况不会产生死锁
t2先申请lock2,lock1,释放lock1,lock2
t1后申请lock1,lock2,释放lock2,lock1
这种情况也不会产生死锁
t1申请到lock1,t2申请到lock2,这样t1就申请不到lock2,t2就申请不到lock1,都等着对方释放资源,这样就产生了死锁
因为让t1,t2申请第一个锁的时候都等待了1秒,所以产生死锁的概率接近100%
3、运行结果:没有执行输出,产生死锁
如何使用IDEA查看程序是否产生死锁?
第一步:点击下方红圈内的Terminal
第二步:在下方命令窗口输入jconsole,然后回车
第三步:双击发生死锁对应的类
第四步:切换到线程,点击下面的检查死锁
🕔第五步:即可看到发生死锁的线程
如何避免死锁?
死锁的产生必须满足互斥使用,不可抢占,请求和保持,循环等待这四个条件,但是只要破坏其中任意一个条件即可破坏死锁,其中最容易破坏的就是循环等待这个条件,那么如何破坏循环等待这个条件呢?
多个线程约定好一定的顺序,按照这个顺序加锁释放锁
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
|
public class DeadLock {
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread() {
@Override
public void run() {
try {
synchronized (lock1) {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("输出t1");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
synchronized (lock1) {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("输出t2");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
t2.start();
}
}
|