死锁的成因,和解决方案总结

news2024/11/23 8:40:37

何为死锁

死锁是多线程或并发程序中的一种情况,当多个线程因为竞争资源而相互等待,并且无法继续执行的情况。在死锁中,每个线程都在等待其他线程释放资源,从而导致所有线程都陷入无限等待状态,无法继续向前执行,最终导致程序无法完成任务。

死锁的三个典型场景

①案例一(一个线程一把锁)

如果一个线程对同一把锁,连续加了两次锁,并且该锁还是不可重入锁的时候,就会产生死锁

public synchronized void increase() {
    synchronized (this) {
        count++;
    }
}

第一次加的锁还没有被释放,又加了一把锁,但此时该锁已经被该线程占用了,所以第二次加锁的时候,只能进行阻塞等待这样第二次的加锁在等第一次加锁的释放,同时第一次加锁又在等第二次加锁的释放,于是就形成了死锁

可重入锁和不可重入锁:

可重入锁是指同一个线程可以多次获取同一个锁,并且每次获取都要对应地释放。当一个线程已经持有锁时,再次请求该锁不会造成死锁或其他异常情况,而是允许这个线程继续获取该锁。synchronized 是可重入锁

不可重入锁是指一个线程只能获取一次锁,如果尝试再次获取同一个锁,会导致线程被阻塞。不可重入锁通常较简单,但在某些情况下可能会导致死锁。

②案例二(两个线程两把锁)

package Thread2;
 
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
 
public class demo22 {
    private static Object locker1 = new Object(); // 相当于醋
    private static Object locker2 = new Object(); // 相当于辣椒
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {  // t1线程相当于是我朋友,再有醋locker1的情况下,还想获取到我的辣椒locker2
            synchronized (locker1) {
                System.out.println("我目前有醋,但我还想蘸辣椒");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { // 正在和我交谈,想要获取辣椒
                    e.printStackTrace();
                }
                synchronized (locker2) {
                    System.out.println("获取辣椒成功!等我吃完饺子就把醋和辣椒都给对方!(释放锁)");
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {  // t2线程相当于是我,再有辣椒locker2的情况下,还想获取到我朋友的醋locker1
            synchronized (locker2) {
                System.out.println("我目前有辣椒,但我还想蘸醋");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1) {
                    System.out.println("获取醋成功!等我吃完饺子就把醋和辣椒都给对方!(释放锁)");
                }
            }
        });
        t2.start();
    }
}

死锁原因分析:

线程t1给对象locker1加了锁,线程t2给对象locker2加了锁;
接着线程t1想要获取对象locker2的锁,但此时locker2被线程t2占用着,t1无法获取,陷入阻塞等待(也无法释放自己占用的对象locker1的锁)

几乎在同一时间,t2想要获取对象locker1的锁,但此时线程t1陷入阻塞,他所占用的locker1的锁无法正常释放。t2获取不到locker1的锁,t2无法正常工作,也无法正常释放自己占用的locker2的锁
就这样t1和t2陷入僵局,谁也无法正常释放锁,形成了死锁
 

解决办法

 给我们的锁编号,按顺序来获取锁(规定都先蘸醋、接着蘸辣椒)

package Thread2;
 
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
 
public class demo22 {
    private static Object locker1 = new Object(); // 相当于醋
    private static Object locker2 = new Object(); // 相当于辣椒
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {  // t1线程相当于是我朋友,一开始都有醋
            synchronized (locker1) {
                System.out.println("我朋友说:我目前有醋,但我还想蘸辣椒");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { // 正在和我交谈,想要获取辣椒
                    e.printStackTrace();
                }
                synchronized (locker2) {
                    System.out.println("我朋友说:获取辣椒成功!等我吃完饺子就把醋和辣椒都给对方!(释放锁)");
                }
            }
        });
        // 死锁的解决办法,多个线程在获取多个锁的时候,我们可以给这些锁编号。每个线程都按照锁的编号,从小到大的获取锁
        // 一开始,我和我朋友要获取到的都是对象locker1的锁,产生竞争,竞争成功的获取到locker1的锁,失败的阻塞等待locker1锁的释放
        // 竞争成功的接着又获取对象locker2的锁,此时因为另一个线程还在阻塞,没人和他竞争,直接获取locker2的锁,然后该线程结束,locker2锁、locker1锁按顺序释放
        // 之前那个竞争失败的线程重写获取到locker1锁,接着又成功获取到locker2锁,最后线程结束,释放锁
        t1.start();
        Thread t2 = new Thread(() -> {  // t2线程相当于是我,一开始都有醋
            synchronized (locker1) { // 先获取编号为1的锁locker
                System.out.println("我说:目前有醋,但我还想蘸辣椒");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker2) {
                    System.out.println("我说:获取辣椒成功!等我吃完饺子就把醋和辣椒都给对方!(释放锁)");
                }
            }
        });
        t2.start();
    }
}

③案例三(N个线程M把锁)

哲学家问题:

五位哲学家坐在一张圆形桌子周围的情景,每个哲学家面前有一只碗和一支筷子。哲学家们交替进行思考和就餐的活动。

问题的关键在于哲学家需要同时拿起他们自己右边和左边的筷子才能进餐,而每两个相邻的哲学家之间共享一支筷子。当多个哲学家同时想拿起相邻的筷子时,可能会出现死锁的情况。例如,如果每位哲学家都拿起他们右边的筷子,那么他们就无法继续进行。

解决办法:

为什么会出现死锁,就是因为线程对锁的互相等待,线程一要获取的锁被线程二占用着,但同时线程二要获取的锁又被线程一占用着,于是他们两个都无法获取到完整的锁,无法完成各自的进程,并释放锁。都处于一个循环等待的过程。

要解决死锁问题,重点就是解决循环等待问题。如果每个线程都按一定的顺序来获取对应的锁,比如我们给5根筷子(5把锁)按从1到5的顺序进行编号,哲学家只能拿到到左右两边锁编号最小的那把锁。这样可以避免环形等待.

 

死锁产生的必要条件

互斥性:当多个线程对同一把锁,有竞争。在某一时刻,最终只有一个线程可以拥有这把锁
不可抢夺性:当一个线程已经获取到了锁A,其他线程要想获取锁A,这个时候只能等该线程把A释放了之后再获取,不能中途抢夺别的线程的锁。
请求和保持性:当一个线程获取到了锁A,除非该线程自己释放锁A,否则该线程就一直保持占有锁A
循环等待性:在死锁中往往会出现,线程A等着线程B释放锁,同时线程B又在等着线程A来释放他所占有的锁,结果A、B的锁都无法正常释放,也都无法完成各自的进程,陷入了一个循环等待的状态

只要这四个条件当中有一个条件被破坏,死锁问题就可以得到解决。为了预防死锁,可以采取一些策略,如资源分配策略、资源优先级、避免占有并等待、强制抢占等.其中循环等待性这个条件最容易被破坏——我们上面的对锁进行编号,来解决死锁问题。利用的就是对循环等待性的破坏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python(七十六)字符串的驻留机制

❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

C++:类与对象补充 - 初始化列表、static成员、友元、匿名对象

目录 引言 一、 初始化列表 1.1 构造函数内部赋值 1.2 使用初始化列表 1.3 注意事项 1.4 explicit关键字 二、 static成员 2.1 概念 2.2 情景 2.3 特性 三、 友元 3.1 概念 3.2 语法 3.2.1 友元函数 3.2.2 友元类 3.3 特性 四、匿名对象 4.1 概念 4.2 语法 …

Soundpad解决自动键失效的问题

这里给出解决方法,具体原因我也不太懂,因为我也是做实验得出某些操作可能会导致自动键不起作用。 首先打开首选项,配置如下图所示,这里只改了特殊热键的五个键位和自动键 我之前犯的错误,我相信大部分跟我一样&#…

74、75、76——tomcat项目实战

tomcat项目实战 tomcat 依赖 java运行环境,必须要有jre , 选择 jdk1.8 JvmPertest 千万不能用 kyj易捷支付 项目机器 选择 一台机器 ,安装jdk1.8的机器下载tomcat的包 上传到机器,解压tomcattomcat文件 bin文件夹: 启动文件 堆栈配置文件 catalina.sh JAVA_OPTS="-Xm…

vue3+ts使用antv/x6

使用 2.x 版本 x6.antv 新官网: 安装 npm install antv/x6 //"antv/x6": "^2.1.6",项目结构 1、初始化画布 index.vue <template><div id"container"></div> </template><script setup langts> import { onM…

DTW(Dynamic Time Warping)动态时间规整

转载于知乎DTW(Dynamic Time Warping)动态时间规整 - 知乎 DTW可以计算两个时间序列的相似度&#xff0c;尤其适用于不同长度、不同节奏的时间序列&#xff08;比如不同的人读同一个词的音频序列&#xff09;。DTW将自动warping扭曲 时间序列&#xff08;即在时间轴上进行局部的…

关于大功率H桥电机驱动模块

关于大功率H桥电机驱动模块 简介接线说明模块接线说明PWM调速控制说明 材料准备实际接线图测试视频总结 简介 大功率H桥电机驱动模块是由两个半桥驱动IC外加4个外部NMOS管组成&#xff0c;发热量小&#xff0c;刹车效果好。 两路PWM输入&#xff0c;占空比可在0-99%内调节。工…

2023/08/10

文章目录 一、计算属性传参二、小程序、h5跳转其他平台授权三、封装popup弹窗四、实现保存海报五、下载图片和复制分享链接 一、计算属性传参 计算属性的值往往通过一个回调函数返回&#xff0c;但是这个回调函数是无法传递参数的&#xff0c;要想实现计算属性传参可以通过闭包…

Python爬虫(十)_正则表达式

什么是正则表达式 正则表达式&#xff0c;又称规则表达式&#xff0c;通常被用来检索、替换那些符合某个模式&#xff08;规则&#xff09;的文本。 正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成…

Prometheus技术文档-基本使用-配置文件全解!!!!!

简介&#xff1a; Prometheus是一个开源的系统监控和告警系统&#xff0c;由Google的BorgMon监控系统发展而来。它主要用于监控和度量各种时间序列数据&#xff0c;比如系统性能、网络延迟、应用程序错误等。Prometheus通过采集监控数据并存储在时间序列数据库中&#xff0c;…

eNSP:双向重定向和路由策略练习

实验要求&#xff1a; 拓扑图&#xff1a; IP、路由器 r1: <Huawei>sys [Huawei]sys r1 [r1]int g 0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int g 0/0/1 [r1-GigabitEthernet0/0/1]ip add 14.1.1.1 24 [r1-GigabitEthernet0/0/1]…

Linux下安装nginx (tar解压版安装)

Linux下安装nginx (tar安装) 1、下载nginx 官方下载地址https://nginx.org/en/download.html 在这里插入图片描述 2.解压 解压‘nginx-1.16.1.tar.gz’到指定目录&#xff08;/usr/local/myWorkSpace&#xff09;并且重命名 命令&#xff1a; tar -xvf nginx-1.16.1.tar.gz …

畜牧虚拟仿真 | 鱼授精过程VR模拟演练系统

随着科技的发展&#xff0c;虚拟现实(VR)技术逐渐渗透到各个领域&#xff0c;为人们提供了更加真实、直观的体验。在动物养殖教育领域&#xff0c;鱼授精过程VR模拟演练系统正成为一种新的教学手段&#xff0c;它能够帮助人们更好地理解和掌握鱼授精的操作技巧&#xff0c;从而…

C# OpenCvSharp读取rtsp流录制mp4

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Extensions; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using Syste…

【雕爷学编程】Arduino动手做(12)---霍尔磁场传感器模块2

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

Linux: network: tools: tcpdump,抓取vlan包需要注意的事情;不然会出现LLC协议

https://bugzilla.redhat.com/show_bug.cgi?id498981#c4 https://serverfault.com/questions/544651/vlan-tags-not-shown-in-packet-capture-linux-via-tcpdump 如果不加-e参数&#xff0c;抓取不到 vlan信息&#xff0c;会导致wireshark解析出现问题。因为&#xff0c;抓到…

调整项目符号/项目编号与文本的距离

百度知道多年前的答案是调整标尺&#xff0c;我的PPT里没有标尺 调节悬挂缩进即可

STM32HAL库:简化STM32微控制器开发

简介&#xff1a;在微控制器开发领域&#xff0c;效率、易用性和兼容性至关重要。STMicroelectronics通过其STM32HAL库为这些问题提供了解决方案&#xff0c;该库是专门为STM32微控制器系列设计的软件开发库。本文旨在探索STM32HAL库的特性、优势和应用程序&#xff0c;并提供使…

【看表情包学Linux】初识文件描述符 | 虚拟文件系统 (VFS) 初探 | 系统传递标记位 | O_TRUNC | O_APPEND

爆笑教程《看表情包学Linux》&#x1f448; 猛戳订阅&#xff01;​​​​​ &#x1f4ad; 写在前面&#xff1a;通过上一章节的讲解&#xff0c;想必大家已对文件系统基本的接口有一个简单的了解&#xff0c;本章我们将继续深入讲解&#xff0c;继续学习系统传递标志位&…

跨境商城服务平台搭建与开发(金融服务+税务管理)

随着全球电子商务的快速发展&#xff0c;跨境贸易已经成为一种新的商业趋势。在这个背景下&#xff0c;搭建一个跨境商城服务平台&#xff0c;提供金融服务、税务管理等一系列服务&#xff0c;可以极大地促进跨境贸易的发展。本文将详细阐述跨境商城服务平台搭建与开发的步骤。…