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

何为死锁

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

死锁的三个典型场景

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

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

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的情况下,还想获取到我的辣椒locker2synchronized (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的情况下,还想获取到我朋友的醋locker1synchronized (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的锁lockerSystem.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.rhkb.cn/news/83689.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0805hw

1. #include <myhead.h> void Bub_sort(int *arr,int n)//冒泡排序 {for(int i1;i<n;i){int count0;for(int j0;j<n-i;j){if(arr[j]>arr[j1]){int temparr[j];arr[j]arr[j1];arr[j1]temp;count;}}if(count0){break;}}printf("冒泡排序后输出结果:\n"…

uni-app离线打包高德地图导入android studio不能正常显示

本人使用的uni-app SDK版本&#xff1a;Android-SDK3.8.7.81902_20230704 1.导入以上文件&#xff0c;依赖已经自动添加了 2.确保这个正常引入 3.修改AndroidMainifest.xml,添加自己的密钥

整理mongodb文档:删

个人博客 整理mongodb文档:删 求关注&#xff0c;哪儿不足&#xff0c;求大佬们指出&#xff0c;哪儿写的不够通俗易懂跟清晰&#xff0c;也求指出 文章概叙 本文主要是介绍了删除数据的几个方法&#xff0c;主要还是在介绍deleteMany、deleteOne以及remove&#xff0c;对于…

JAVA基础之放弃使用Random

随机是日常生活中经常遇到的非常有趣的东西&#xff0c;比如说抛硬币&#xff0c;他的不可预知性总是让我们特别着迷&#xff0c;在拿不定主意时&#xff0c;有些人就喜欢用抛硬币的方式来帮助我们做决定。体育领域也喜欢用喜欢用抛硬币的方式来猜先。随机数功能是Java非常非常…

14个前端开发者应该知道的实用网站

在本文中&#xff0c;我将分享一些非常有用的网站合集&#xff0c;这些网站可以在你的日常工作中极大地帮助你。这些网站已经成为我各种任务的首选资源&#xff0c;节省了我的时间&#xff0c;提高了工作效率 文档自动化 Documatic 是一款专为开发人员设计的非常高效的搜索引擎…

Pytorch深度学习-----现有网络模型的使用及修改(VGG16模型)

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

STM32 CubeMX USB_MSC(存储设备U盘)

STM32 CubeMX STM32 CubeMX USB_MSC(存储设备U盘&#xff09; STM32 CubeMX前言 《使用内部Flash》——U盘一、STM32 CubeMX 设置USB时钟设置USB使能UBS功能选择FATFS功能 二、代码部分修改代码"usbd_storage_if.c"修改代码"user_diskio.c"main函数初始化插…

每天一道leetcode:剑指 Offer 27. 二叉树的镜像(适合初学者递归树)

今日份题目&#xff1a; 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 例如输入&#xff1a; 4 / \ 2 7 / \ / \ 1 3 6 9 镜像输出&#xff1a; 4 / \ 7 2 / \ / \ 9 6 3 1 示例 输入&#xff1a;root [4,2,7…

c基础扫雷

和三子棋一样&#xff0c;主函数先设计游戏菜单界面&#xff0c;这里就不做展示了。 初始化棋盘 初级扫雷大小为9*9的棋盘&#xff0c;但排雷是周围一圈进行排雷(8格)&#xff0c;而边界可能会越界。数组扩大了一圈,行和列都加了2&#xff0c;所以我们用一个11*11的数组来初始化…

数据结构—树和二叉树

5.树和二叉树 5.1树和二叉树的定义 树形结构&#xff08;非线性结构&#xff09;&#xff1a;结点之间有分支&#xff0c;具有层次关系。 5.1.1树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。 若n0&#xff0c;称为空树&#x…

Vue2嵌入HTML页面空白、互相传参、延迟加载等问题解决方案

一、需求分析 最近做的一个用H5加原生开发的html项目&#xff0c;现需要集成到Vue2.0项目里面来。遇到的相关问题做个记录和总结&#xff0c;以便能帮到大家避免踩坑。 二、问题记录 1、页面空白问题 将html页面通过iframe的方式嵌入进来之后&#xff0c;发现页面是空白的&am…

面试热题(倒数第k个结点)

输入一个链表&#xff0c;输出该链表中倒数第k个节点。为了符合大多数人的习惯&#xff0c;本题从1开始计数&#xff0c;即链表的尾节点是倒数第1个节点。 例如&#xff0c;一个链表有 6 个节点&#xff0c;从头节点开始&#xff0c;它们的值依次是 1、2、3、4、5、6。这个链表…

opencv动态目标检测

文章目录 前言一、效果展示二、实现方法构造形态学操作所需的核:创建背景减除模型:形态学操作:轮廓检测: 三、代码python代码C代码 总结参考文档 前言 很久没更新文章了&#xff0c;这次因为工作场景需要检测动态目标&#xff0c;特此记录一下。 一、效果展示 二、实现方法 基…

图的深度优先遍历和广度优先遍历

目录 图的创建和常用方法 深度优先遍历&#xff08;Depth First Search&#xff09; 广度优先遍历&#xff08;Broad First Search&#xff09; 图的创建和常用方法 //无向图 public class Graph {//顶点集合private ArrayList<String> vertexList;//存储对应的邻接…

JVM工作的总体机制概述

JDK、JRE、JVM关系回顾 JVM&#xff1a;Java Virtual Machine&#xff0c;翻译过来是Java虚拟机JRE&#xff1a;Java Runtime Environment&#xff0c;翻译过来是Java运行时环境 JREJVMJava程序运行时所需要的类库JDK&#xff1a;Java Development Kits&#xff0c;翻译过来是…

进程 的初识

程序和进程有什么区别 程序是静态的概念&#xff0c;gcc xxx.c -o pro 磁盘中生成的文件&#xff0c;叫做程序。进程是程序的一次运行活动&#xff0c;通俗点的意思就是程序跑起来了&#xff0c;系统中就多了一个进程。 如何查看系统中有哪些进程 使用 ps 指令&#xff08;完整…

解决vue3+echarts关于无法获取dom宽度和高度的问题

解决vue3echarts关于无法获取dom宽度和高度的问题 近期写vue3项目&#xff0c;很多地方都用到了echarts&#xff0c;刚开始写的时候&#xff0c;发现图一直出不来&#xff0c;报错/报警内容一般有两项&#xff1a; Uncaught (in promise) Error: Initialize failed: invalid …

恒盛策略:欧洲能源危机又来?天然气价格飙升,受益板块曝光

储能板块有望获益。 今日早盘煤炭、交通运输、石油石化等板块涨幅均超1%&#xff0c;其中煤炭板块涨1.37%位居第一位。音讯面上&#xff0c;欧佩克重申减产战略&#xff0c;世界原油价格升至3个月来高位。此外&#xff0c;隔夜欧洲天然气期货跳涨40%&#xff0c;创2022年3月以来…

7.6 通俗易懂解读残差网络ResNet 手撕ResNet

一.举例通俗解释ResNet思想 假设你正在学习如何骑自行车&#xff0c;并且想要骑到一个遥远的目的地。你可以选择直接骑到目的地&#xff0c;也可以选择在途中设置几个“中转站”&#xff0c;每个中转站都会告诉你如何朝着目的地前进。 在传统的神经网络中&#xff0c;就好比只…

如何设置文字颜色和背景颜色?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 设置文字颜色&#xff08;color属性&#xff09;⭐ 设置背景颜色&#xff08;background-color属性&#xff09;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你…