如何理解死锁?
创始人
2024-03-21 12:15:58
0

目录

今日良言:等风来,不如追风去

一、死锁

1.概念

2.死锁的三个典型情况

3.死锁的必要条件

4.如何破除死锁


🐳今日良言:等风来,不如追风去

 

🐕一、死锁

🐇1.概念

多个线程在争夺资源时,陷入了僵持状态,都无法进行下去,都在等待对方释放资源,如果不干涉,将一直僵持下去

🐇2.死锁的三个典型情况

通过这三个典型情况,可以帮助理解死锁

1).一个线程一把锁,连续加锁两次,如果锁是不可重入锁就会死锁

2).两个线程两把锁,t1和t2先分别针对锁A和锁B进行加锁,再尝试获取对方的锁.

    t1先拿到锁A,然后获取锁B,t2先拿到锁B,然后获取锁A,此时就会发生死锁.

    举个例子:有一天小马和女朋友出去吃饺子,两人都喜欢用酱油和醋蘸着饺子吃,饺子上好以

    后,小马拿起了醋,女朋友拿起了酱油,然后女朋友对小马说:你把醋给我,我用完了就给你. 然

    后,小马说:你把酱油给我,我用完了给你.如果此时两个人互不相让,此时就发生死锁.

    针对这种情况,通过下面代码来加深理解:

public class ThreadDemo21 {public static void main(String[] args) {// 创建两个实例:jiangyou和cuObject jiangyou = new Object();Object cu = new Object();// 创建两个线程Thread t1 = new Thread(() -> {// 先拿到cusynchronized (cu) {try {// 为了让t2拿到jiangyouThread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (jiangyou) {System.out.println("小马拿到了醋和酱油");}}});Thread t2 = new Thread(() -> {// 先拿到cusynchronized (jiangyou) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (cu) {System.out.println("女朋友拿到了醋和酱油");}}});t1.start();t2.start();}
}

此时运行代码,会发现程序无任何运行结果,说明没有线程拿到两把锁.

使用jconsole观察当前线程的情况

 如果打开jconsole乜有进程的话,以管理员身份运行

3).多个线程多把锁

经典案例:哲学家就餐问题

问题描述:假设现在餐桌上有五个哲学家,每个哲学家的面前都放着一碗面条,桌子上有5根筷子,每个哲学家都需要两根筷子才可以吃面条,每个哲学家都有两种状态:1.思考人生(相当于线程的阻塞状态)   2.拿起筷子吃面条(相当于线程获取到锁,执行指令)    由于操作系统的随机调度,这5个哲学家随时都可能想吃面条或者思考人生,要想吃面条必须拿起左右手的两根筷子,情况图如下:

 

假设在某一时刻,发生极端情况,此时就会发生死锁

例如:同一时刻,所有哲学家都拿起左手的筷子,此时哲学家们都拿不到右手的筷子,都需要等待右边的哲学家将筷子放下.

🐇3.死锁的必要条件

死锁有四个必要条件:

1).互斥使用

    线程1拿到了锁,线程2就只能阻塞等待,无法使用锁.

2).不可抢占

    线程1拿到锁后,线程2不能强夺锁,只能阻塞等待,等待线程1释放锁

3).请求和保持

    线程1拿到锁A后,再尝试获取锁B,此时锁A还是保持的(并不会因为要获取锁B就释放锁A)

4).循环等待

    线程1尝试获取锁A和锁B   线程2尝试获取锁B和锁A

    线程1在获取锁B的时候等待线程2释放锁B,线程2在获取锁A的时候等待线程1释放锁A

🐇4.如何破除死锁

在死锁的四个必要条件中,前三个都是锁的基本特性,我们无法更改,所以说破除死锁的突破口就是:循环等待

如何破除循环等待呢?

最简单的办法:给锁编号,然后指定一个固定的顺序(比如从小到大)来加锁,任意线程加多把锁的时候,都让线程遵守上述顺序,此时循环等待自然破除.

以哲学家就餐问题为例:破除循环等待,为每一个筷子编号,从小到大,然后从第一个哲学家开始,规定:每次先拿编号小的筷子,然后拿编号大的筷子.

 

此时,对于哲学家5来说,无法拿起筷子,因为编号1的筷子已经被哲学家1拿走了,此时哲学家5阻塞等待,此时,对于哲学家4来说,他就可以拿起编号为5的筷子,然后吃面条,吃完后,放下筷子,此时哲学家3可以吃面条,以此类推,最后死锁就会被破除了.

针对上面的两个线程两把锁的代码,破除循环等待的代码如下.

public class ThreadDemo21 {public static void main(String[] args) {// 规定:jiangyou 是1号  cu 是2号  约定:先拿小的,再拿大的Object jiangyou = new Object();Object cu = new Object();// 创建两个线程Thread t1 = new Thread(() -> {// 先拿到cusynchronized (jiangyou) {try {// 为了让t2拿到jiangyouThread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (cu) {System.out.println("小马拿到了醋和酱油");}}});Thread t2 = new Thread(() -> {// 先拿到cusynchronized (jiangyou) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (cu) {System.out.println("女朋友拿到了醋和酱油");}}});t1.start();t2.start();}
}

 此时运行代码:

 死锁的问题就得到了解决.

还有一种办法破除死锁的办法,银行家算法,但是过于复杂,这里只介绍简单的解决死锁的办法

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...