您的位置 首页 java

线程的“死锁”,今天一下就顿悟了

我网上查到的死锁

好,如果你能看懂理解这句话,就没必要看下去了,你已经知道啥是死锁了

死锁的四个条件

1.互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

2.请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

3.不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)

4.循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有

好,如果你还能理解这句句话,那你真没必要看下去了,你理解能力很强。再结合这些文章下面的一些代码例子。你就完全懂了

但是我没懂,或者说当时懂了,过段时间就忘了。

线程的“死锁”,今天一下就顿悟了

线程的“死锁”,今天一下就顿悟了

线程的“死锁”,今天一下就顿悟了

我理解的死锁

线程的“死锁”,今天一下就顿悟了

是的,就像 2个人拿枪互相指着,想要对方手上的枪

线程的“死锁”,今天一下就顿悟了

四个条件

为什么说这就是我理解的死锁呢?毕竟产生死锁,需要符合4个条件,那我理解的死锁,是否满足呢。下面来一一对应一下。(下面的4个条件,与上文的4个大致相似,但有细微区别,我采用《 Java 编程之美》里的描述)


tip:男人女人各为2个不同线程,2把抢为2个资源


《Java编程之美》 死锁的4个条件

1.互斥条件:指线程对已经获取到的资源进行排它性使用,即该资源只能同时只由一个线程占用

一把手枪只能被一个人拿着,另外的人想要用,只能等现在正在使用的人丢了,或者从他手上抢过来。(条件1成立)

2.请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新的资源已经被其他线程占用,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源

男的已经拿了一把枪,但是又提出了让女的把枪给自己的要求,而这把枪已经被女的拿着了。所以男的就等着,等的时候肯定不会把自己的枪给女方。(条件2成立)

3.不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放资源。

2个人拿枪互指着,还能让对方剥夺了自己的枪?也许电影里可能有主角光环,但是两个线程可没有大鱼和小鱼的区别。(条件3成立)

4.环路等待条件:指发生死锁时,必然存在一个线程一资源的环形链,即线程集合{T0,T1,T2..Tn}中的T0正在等待一个T1占用的资源,T1在等T2占用的资源,。。。Tn在等T0占用的资源。

很明显男的想拿女的手上的枪,女的想要拿男的手上的枪。(条件4成立)

如果你觉得2个人好像觉得条件4好像有点不好理解,那么

(假装 明楼 手上拿着枪指着明诚。。)

那,明诚想要明台手上的枪,明台想要明楼手上的枪,明楼想要明诚手上枪。。你看,是不是,T1->T2->T3->T1 成了个环形链呢。看他们站位也是个环。

纸上学来终觉浅,绝知此事要躬行

上代码

《Java并发编程之美》

 class LockDemo {
   //创建资源
   private static Object gunA = new Object();
   private static Object gunB = new Object();

   public static void main(String[] agrs) {
       //创建线程
        Thread  threadMan = new Thread(new Runnable() {
           @ Override 
           public void run() {
                synchronized  (gunA) {
                   System.out.println(Thread.currentThread() + "男人拿到了枪A");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread() + "男人想要枪B");
                   synchronized (gunB) {
                       System.out.println(Thread.currentThread() + "男人拿到枪B");
                   }
               }
           }
       });
       Thread threadWoMan = new Thread(new Runnable() {
           @Override
           public void run() {
               synchronized (gunB) {
                   System.out.println(Thread.currentThread() + "女人拿到了枪B");
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread() + "女人想要枪A");
                   synchronized (gunA) {
                       System.out.println(Thread.currentThread() + "女人拿到了枪A");
                   }
               }
           }
       });
       //启动线程
       threadMan. start ();
       threadWoMan.start();
   }
}  

运行结果:

 Thread[Thread-0,5,main]男人拿到了枪A
Thread[Thread-1,5,main]女人拿到了枪B
Thread[Thread-1,5,main]女人想要枪A
Thread[Thread-0,5,main]男人想要枪B  

男女各自有一把枪指着对方,然后都试图获取对方手里的枪,但是没有后文。这就是一个死锁。

代码分析:

代码先创建了2把抢,也就是2个资源,并创建了2个线程(看结果可以知Thread-0为男,Thread-1为女)。男的先获取到一把枪后,线程sleep了1s,为的是保证女的也可以拿的枪。这样才可以继续后面的条件,这个时候,男的获得了枪A,女的获得了枪B(线程0获取了资源gunA的锁,线程1获取了资源gunB的锁)。男在拿到枪A后,视图获取枪B,而枪B已经被女的占用了,女的也视图获取枪A。显然也获取不到。于是陷入等待。也就产生了死锁。

所以color{red}{所以} 所以

2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”color{red}{2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”} 2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”

如何打破死锁

我们来完善一下上面的场景,2个人找到了一个通往没有bug的世界的大门,但是大门紧闭,门口放了两把枪,只有同时拿到两把枪,才可以进入大门(人进去后,两把枪又回到了原来的地方)。那按照电视剧里的剧情,是不是就会发生上面这种场面呢。一人枪一把后,互补相让。

那么如何才可以避免这种僵局,让2个人都通往有bug的世界呢,其实可以一个人先拿着2把枪进去后,另一个人再去拿不就好了。

所以,打破死锁只要打破4个条件之一就行,而4个条件能打破的只有条件2“请求并持有”和条件4“环路等待”可以打破。

造成死锁的原因其实和上门例子里的场景很类似,和申请资源的顺序有很大原因。2个人如果有序的去拿枪进门。就避免了死锁的产生。代码如何体现呢?修改一下线程WoMan

  Thread threadWoMan = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (gunA) {
                    System.out.println(Thread.currentThread() + "女人拿到了抢A");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "女人想要枪B");
                    synchronized (gunB) {
                        System.out.println(Thread.currentThread() + "女人拿到了枪B");
                    }
                }
            }
        });  

运行结果

 Thread[Thread-0,5,main]男人拿到了枪A
Thread[Thread-0,5,main]男人想要枪B
Thread[Thread-0,5,main]男人拿到了枪B
Thread[Thread-1,5,main]女人拿到了抢A
Thread[Thread-1,5,main]女人想要枪B
Thread[Thread-1,5,main]女人拿到了枪B  

代码分析:

男女都去获取枪A。假设男先获取到枪A,则女就会被阻塞,陷入等待,而不会去获取枪B,男的获取到枪A后,再去获取枪B,也顺利的获取到了,进入了没有bug的世界。这个时候2把抢又回到了原来的地方(两把枪资源的锁被释放了)。女就可以去获取枪A再获取枪B了。

这里几个小点:

  • 1.男女两个线程start后。不一定是先start的就先获取到资源枪A
  • 2.男线程获取到枪A后,女线程不会去获取枪B,因为代码里女线程需要先获取枪A,而此时枪A被男线程获取了,女线程就陷入阻塞
  • 3.男线程获取资源枪A后,获取资源枪B的时候,不会立即释放枪A。这里有个容易误解的地方,一个线程只能同时获取一个资源么,也就是一个线程同时只能持有一把锁么,答案为:不是的。 因为锁是针对“对象”的,锁和对象是一一对应的,所以线程可以同时持有多个锁。

作者:ZhaoYun

链接:

来源:掘金

文章来源:智云一二三科技

文章标题:线程的“死锁”,今天一下就顿悟了

文章地址:https://www.zhihuclub.com/179565.shtml

关于作者: 智云科技

热门文章

网站地图