创造一个死锁的情形

死锁概念

多个线程互相持有对方所需要的资源,导致这些线程处于等待状态。

产生的必要条件

  1. 互斥:一个资源只允许一个线程访问(厕所一个坑只能一个人上厕所)
  2. 占有且等待:一个线程占有资源,同时还有其他线程未得到满足,正在等待其他线程释放该资源(厕所坑里进了一个人,外面还有一个人等着里面的人厕所用完)
  3. 不可抢占:其他线程已经占有了某项资源,不能因为你需要就抢过来(别人已经进坑里上厕所了,不能因为你也要上,直接把那人揪出来,不合适吧,哈哈)
  4. 循环等待:发生死锁时,所等待的线程必定会形成一个环路,造成永久阻塞

可以通俗的概括为:

  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 都不放弃自己拥有的资源

死锁实现

死锁造成的影响很少会显现出来,如果一个类可能发生死锁,那么一般是在高并发,很糟糕的时候发生死锁。

手写一个简单的死锁,下面这段代码加了延时,只要运行就会产生死锁:

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
package DeadLock;
/*写一个两个线程死锁的程序
* coded by Jerome
*/
class MyDeadLock implements Runnable{
boolean flag;
static Object o1 = new Object();
static Object o2 = new Object();
public MyDeadLock(boolean flag){
this.flag = flag;
}
public void run(){
if(this.flag){
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("我没发生死锁");
}
}
}
else{
synchronized (o2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("我也是");
}
}
}
}
}
public class Test {

public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1 = new Thread(new MyDeadLock(true));
Thread t2 = new Thread(new MyDeadLock(false));
t1.start();
t2.start();
}

}

运行这段代码,控制台什么都没显示,说明发生了死锁。

sleep()是不放弃拥有的资源的,若要修改死锁状态,可以使用wait()(释放资源,进入线程池等待)。

死锁预防

互斥条件是必须的,不能被改变,还应该加以保证

  1. 破坏“占有且等待”:

    所有线程在开始运行之前,必须一次性的申请在整个运行过程中所需要的全部资源,
    允许线程只获取运行初期需要的资源,在运行过程中逐步的释放掉使用完毕的资源;

  2. 破坏“不可抢占条件”:

    当一个已经持有了一些资源的线程在提出新的资源请求没有得到满足时,它必须释放掉已经保持的所有资源;

  3. 破坏“循环等待”:

    定义资源类型的线性顺序来预防。

方法:

  • 固定加锁顺序
  • 开放调用
  • 使用定时锁
  • 死锁检测

参考链接

  1. pengboboer:手写死锁
  2. plugcy:手写一个死锁程序

土豪将鼓励我继续创作和搬运!