17.死锁是如何产生的


 

 

死锁是如何产生的

 引言

本节介绍一个和同步所相关的知识点死锁。下面从3点来介绍死锁。

第一点什么是死锁?

第二点死锁是如何产生的?

第三点编写一个死锁示例。

什么是死锁?

首先来看第一点,什么是死锁?死锁是指两个或两个以上的线程,在执行过程中,由于竞争资源或者是由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生的死锁,这些永远在互相等待的线程称为死锁线程。

简而言之就是两个或两个以上的线程争夺彼此的锁造成阻塞,程序永远处于阻塞状态。

案例

这就好比一个女孩一个男孩,女孩手上有男孩想要的魔方,男孩手上有女孩想要的棒棒糖,然后女孩说我想要棒棒糖,接着男孩说我想要魔方,于是女孩对男孩说,你把棒棒糖给我,我就给你魔方。这时男孩也对女孩说,你把魔方给我,我就给你棒棒糖,于是他们谁都不让谁僵持住,这个过程就和死锁很像。

 线程

下面我们把男孩女孩换成线程,看看线程a持有a锁,想要b锁,线程b持有b锁下想要a锁,

他们互不相让,僵持住,形成了死锁局面。

死锁是如何产生的?

第一点介绍完了。我们再来看第二点,死锁是如何产生的? 

死锁的产生主要有4个条件,

第一个条件是两个或两个以上的线程,

第二个条件是两个或两个以上的锁。

第三个条件是两个或两个以上的线程持有不同锁。

第四个条件是持有不同锁的线程争夺对方的锁。

编写一个死锁事例

下面我们根据死锁的产生条件,来编写一个死锁事例,按照这4个条件一步一步编写。

第一个条件

首先是第一个条件,两个或两个以上的线程定义两个类,

分别是locka和lockb然后继承Runnable

类,于是线程a和线程b两个线程就准备好了。 

第二个条件

第一个条件应满足,再来看第二个条件,两个或两个以上的锁。分别在locka和lockb两个类里面边写一个静态同步方法,printera和printb.于是同步锁locka, lockb两个锁就准备好了。接着分别在两个方法里面输出a和b这个输出主要是用于观察方法是否被正常调用。在输出之前,我们使当前线程休眠一秒钟,此步骤为的是让线程不要太快执行完,待会还要在下面调用对方的方法去争夺对方的锁。

 第三个条件

第二个条件以满足,再来看看第三个条件,两个或两个以上的线程持有不同的锁,很简单,直接重写run方法,然后在run方法里面分别调用printa和printb即可。

于是线程a持有锁就是locka.class,因为是静态同步方法,所以锁的类型就是自身类名点class,同样的线程lockb就只有同步锁lockb.class,

现在两个线程已经持有了不同的数。第三个条件已满足。

第四个条件

再来看看第四个条件,只有不同锁的线程去争夺对方的锁,我们只需在printA A方法里面去调用lockB .printB方法,在printAb方法里面去调用printA .printa方法即可,如此一来线程a就去争夺lockb的锁,线程b就去争夺locka的锁,最后1个条件也已经满足了,现在4个条件全部满足。 

代码

 locka

package com.chenjie.executor.day17;

/**
 * packageName com.chenjie.executor.day17
 *
 * @author chenjie
 * @version JDK 8
 * @className LockA (此处以class为例)
 * @date 2024/5/29
 * @description TODO
 */
public class LockA implements Runnable {
    @Override
    public void run() {
        printA();
    }

    public static synchronized void printA(){
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("a");
        LockB.printB();
    }
}

lockb

package com.chenjie.executor.day17;

/**
 * packageName com.chenjie.executor.day17
 *
 * @author chenjie
 * @version JDK 8
 * @className LockA (此处以class为例)
 * @date 2024/5/29
 * @description TODO
 */
public class LockB implements Runnable {
    @Override
    public void run() {
        printB();

    }

    public static synchronized void printB(){
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("b");
        LockA.printA();
    }
}

main

package com.chenjie.executor.day17;


import com.chenjie.executor.day16.Task;

/**
 * packageName com.chenjie.executor.day09
 *
 * @author chenjie
 * @version JDK 8
 * @className Main1 (此处以class为例)
 * @date 2024/5/27
 * @description TODO
 */
public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        LockA lockA=new LockA();
        LockB lockB=new LockB();
        Thread thread = new Thread(lockA,"thread");
        Thread thread1 = new Thread(lockB,"thread1");
        thread.start();
        thread1.start();

    }
}

结果

下面来启动这两个线程,首先将线程创建出来,然后启动他们。

从运行结果来看,两个线程分别在打印了a和b之后,进入相互争夺锁的过程,双方僵持住形成了死锁程序,想要停下来需手动关闭,也就是借助外力。

 总结

最后来总结一下本节的内容。

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

本节演示了死锁,死锁的定义在这里就不再赘述了。希望大家在以后使用同步的时候避免死锁。 

  

posted @ 2022-05-03 15:09  小陈子博客  阅读(213)  评论(0编辑  收藏  举报