Java死锁的原因及解决方法

        要想知道死锁出现的原因和解决方法,首先得知道什么是死锁,死锁是两个或两个以上的运算单元(进程、线程或协程),互相持有对方所需的资源,导致它们都无法向前推进,从而导致永久阻塞的问题。从字面意义上去理解并不太好理解,用实际情况来讨论可以更好的理解。

public synchronized void print(){synchronized (this){System.out.println("hahha");}
}

当代码执行到这个方法,会对该方法进行加锁,避免其他的线程加入进来干扰代码的运行,但是当代码执行到代码块里面时,发现这里又需要加锁,而且加的还是同一个锁(当synchronized对普通方法进行加锁时,相当于对this进行加锁,当对静态方法进行加锁时,相当于对类对象进行加锁),那么就会发生冲突,产生锁竞争。因为该this已被加锁,要想执行下去,就必须对this进行解锁,而要进行解锁,就得代码继续执行下去,这就导致代码逻辑产生矛盾,使得它们各自都无法向前推进,从而导致永久堵塞,这就产生了死锁,也是死锁的一种体现形式,这种死锁一眼就能看出,而方法的调用而导致出现的死锁就让人防不胜防。

public synchronized void func1(){func2();
}
public void func2(){synchronized (this){};
}

或许现在可以一眼就能发现死锁,但是当调用的方法更多时,还能保证不会出现死锁吗?不过这种情况是不科学的,因为此时进行两次加锁的是同一个线程,为什么说它是不科学的呢?就像你有了一把钥匙,这把钥匙打开了大门,但此时里面还有一扇挂着相同的锁的大门,你就不能用这把钥匙打开下一扇大门了吗?当然可以,因此,当第二次尝试加锁时,该线程已经有了这个锁的权限了,这个时候不应该加锁失败,也不应该阻塞等待。为了应对这种情况,Java也就产生了两种锁,一种是可重入锁,一种是不可重入锁。不可重入锁就是锁不会被保存,当它被某个线程给加了锁后,只要它处于加锁状态时,当它再次收到加锁的请求后,就会拒绝当前加锁,即使是该加锁的请求是对它已经加锁了的线程。而可重入锁则是会保存当前锁,当它被某个线程给加了锁后,当它再次收到加锁的请求,则会对比当前要进行加锁操作的线程和已经加了锁的线程是否一样,这样就可以灵活判断了,如果相同就不进行加锁且直接执行,如果不相同,那就得堵塞了,而synchronized就是可重入锁,因此上述代码不会出现这种情况。如果出现这种情况的话,什么时候进行解锁呢?

public void func2(){synchronized (this){synchronized (this){synchronized (this){……;}}};
}

这很容易想到,当然是要出了最外面的synchronized才能进行解锁,不过我们虽然知道要到最外面的synchronized才解锁,但是jvm怎么知道什么时候才到了最外面的synchronized呢?其实很简单,增加一个计数器就行,只需要在每次进行加锁操作时,让计数器进行加一,每一个出锁操作就让计数器减一,这不就很好的解决了吗?到这死锁的一种典型情况就结束了,那就是一个线程一把锁,但是该锁是不可重入锁,并且一个线程对该锁进行了多次加锁,导致死锁的出现。

        现在就来谈谈死锁的第二种情况:两个线程两把锁,但是这两个线程同时获取对方的锁,这也将导致死锁的发生。

public void func3(){synchronized (locker1){synchronized (locker2){;}}
}
public void func4(){synchronized (locker2){synchronized (locker1){;}}
}

因为两个线程同时分别调用这两个方法,导致这两把锁同时被加锁,当它们又要再次获取另一把锁时,就会因为该锁被占用而导致线程进入阻塞状态,直到要获取的锁被释放,而因为这两个线程要想进行下去,就必须获取锁,要想获取锁就要进行下去,就如钥匙放在家里,要想进去家里,就得拿钥匙,要想拿钥匙,就得进家里,因而导致死锁的出现。

        而死锁的出现还有第三种情况,那就是N个线程M把锁,比如将一堆人围成一圈吃饭,在每个人的间隔中放一根筷子,而想要吃饭就必须要那两根,并且他们中的人什么时候开始吃饭都是不确定的,而当他们开始吃饭时,拿到筷子,除非吃完否则绝不放下,哪怕只拿到一根,而没拿到的就只能等待有筷子的时候。倘如有些人拿到了两根筷子,那么势必有人只有一根筷子甚至没有筷子,这就造成堵塞,将一跟筷子看成是一把锁,只有获得两把锁的线程才能顺利进行下去,那么那些只有一把锁甚至没有锁的线程就会堵塞,倘如有两个线程出现死锁出现的第二种情况,就会产生死锁,这就是N个线程M把锁出现死锁的情况。

        根据死锁出现的三种情况,可以发现其中有着共同之处,可以认为是死锁出现的必要原因。

1)互斥使用:当一个线程获取到一把锁后,其他线程在该线程释放该锁时,是不能获取该锁。

2)不可抢占:锁只能被所有者主动释放,而不能被其他线程给强行释放并直接加锁。

3)请求和保持:当线程获取一把锁后,在获取另一把锁时,会保持对第一把锁的的获取状态。

4)循环等待:t1想要获取locker2,就要等待t2释放locker2, t2想要获取locker1就要等待t1释放locker1,因此它们就得循环等待。

要想出现死锁,上述原因缺一不可。既然知道了死锁出现的原因,那么只需要破坏其中一个原因就可以避免死锁的出现了。因为原因1和原因2是锁的特性,无法修改,那么只能对原因3和4下手了,原因3和原因4取决与代码的结构。不过有时原因3会影响到需求,因此大部分时候对原因4进行改动。比如对所有的锁进行编号,并且规定必须得先对小的加锁,才能对大的加锁,只要代码严格按照该方法进行加锁,那么就可以规避循环等待。

private void func5(){synchronized (locker1){synchronized (locker3){synchronized (locker5){;}}}
}private void func6(){synchronized (locker2){synchronized (locker3){synchronized (locker4){;}}}
}

到此死锁就此结束。

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

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

相关文章

【数据库事务日志碎片原理分析与方案】-分析篇

前言:说都数据库的事务日志,可以说我们是再熟悉不过的了。一般而言,我们都没有必 要去关心事务日志中的虚拟日志文件的个数。这里提到的“虚拟日志文件”的概念,我们 后面会进行专门的讲述。很多的时候,我们在建立数据库的时候&am…

element树形筛选

<el-inputv-model"projectName"placeholder"请输入名称"clearablemaxlength"10"clear"clearTree" /> <el-divider /> <el-treeref"tree"class"filter-tree":data"treeList":props"…

VSCode远程调试python

配置 1&#xff0c;进入到服务器相应的docker和conda环境中 2&#xff0c;安装debugpy pip install debugpy 3&#xff0c;VSCode 中点击调试按钮 4&#xff0c;按照提示&#xff0c;安装扩展 5&#xff0c;配置launch.json 最终的文件launch.json&#xff1a; {// 使用…

跟着视频学习java,发现swagger打不开?怎么解决

前因 现在到处都在卷java&#xff0c;不会java的前端不是好前端。 这不&#xff0c;周围有前端同学开始学java了。 昨天他突然找我问说引入依赖&#xff0c;配置之后swagger打不开了。 分析过程 1、 查看他的swagger版本&#xff0c;让他试了对应路径/swagger-ui/index.h…

爬虫数据清洗可视化实战-就业形势分析

基于采集和分析招聘网站的数据的芜湖就业形势的调查研究 一、引言 本报告旨在分析基于大数据的当地就业形势&#xff0c;并提供有关薪资、工作地点、经验要求、学历要求、公司行业、公司福利以及公司类型及规模的详细信息。该分析是通过网络爬虫技术对招聘网站的数据进行采集…

基于Simulink的用于电力系统动态分析

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Unity3D赛车游戏优化篇】【八】汽车实现镜头的流畅跟随,以及不同角度的切换

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

华为mate60 上线 媒介盒子多家媒体报道

为什么你的品牌营销不见效&#xff1f;如何能推动品牌破圈&#xff1f;让媒介盒子给你一些启发。本期盒子要跟大家分享地新机上市&#xff0c;数码科技行业企业该如何做线上宣传。 HUAWEI Mate 60系列8月29日官宣发布&#xff0c;出色的拍照功能、强大的性能表现和持久的续航能…

【C++二叉树】进阶OJ题

【C二叉树】进阶OJ题 目录 【C二叉树】进阶OJ题1.二叉树的层序遍历II示例代码解题思路 2.二叉搜索树与双向链表示例代码解题思路 3.从前序与中序遍历序列构造二叉树示例代码解题思路 4.从中序与后序遍历序列构造二叉树示例代码解题思路 5.二叉树的前序遍历&#xff08;非递归迭…

科技云报道:AI+云计算共生共长,能否解锁下一个高增长空间?

科技云报道原创。 在过去近一年的时间里&#xff0c;AI大模型从最初的框架构建&#xff0c;逐步走到落地阶段。 然而&#xff0c;随着AI大模型深入到千行百业中&#xff0c;市场开始意识到通用大模型虽然功能强大&#xff0c;但似乎并不能完全满足不同企业的个性化需求。 大…

【优选算法】—— 前缀和算法

前言&#xff1a; 本期&#xff0c;我将要带大家学习的是有关前缀和算法的学习&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;什么是前缀和算法 &#xff08;二&#xff09;题目讲解 1、【模板】前缀和 2、【模板】二维前缀和 3、 和可被K整除的…

动态内存申请

动态内存申请 静态分配 1、 在程序编译或运行过程中&#xff0c;按事先规定大小分配内存空间的分配方式。int a [10] 2、 必须事先知道所需空间的大小。 3、 分配在栈区或全局变量区&#xff0c;一般以数组的形式。 4、 按计划分配 动态分配 1、在程序运行过程中&#xff0c…

python读取.txt文件中某些关键字后面的内容 并根据该数据画图

感谢一下悦姐帮忙 import re#先把文件读进来&#xff0c;用read读入的是字符串&#xff0c;readlines是list with open(resok.txt) as f:txt f.read()dataset r5low:.*|5mix:.*|5normal:.* para rMAE: (.{6})#意思是MAE&#xff1a; 后面的六个东西 row_data re.findall(d…

【Arduino27】DHT11温湿度传感器模拟值实验

硬件准备 DHT11温湿度&#xff1a;1个 面包板&#xff1a;1个 杜邦线&#xff1a;3根 硬件连线 VDD引脚接 5V 电源 DATE引脚接 4号 接口 GND引脚接 GND 接口 软件程序 #include<DHT.h>#define DHT11_pin 4 //温湿度传感器引脚DHT dht(DHT11_pin,DHT11);float tem…

QuantLib学习笔记——看看几何布朗运动有没有股票走势的感觉

⭐️ 小鹿在乱撞 小伙伴们肯定看过股票的走势&#xff0c;真是上蹿下跳啊&#xff0c;最近小编学了一丢丢关于随机过程和QuantLib的知识&#xff0c;想利用随机过程生成一个类似股票价格走势的图&#xff0c;安排&#xff01;&#xff01;&#xff01; ⭐️ 随机过程 随机过程…

JS中call方法是什么,call()的原理是什么?如何手写一个call()?Symbol是什么,怎么用Symbol调优?含详细解析

&#x1f389;call() &#x1f495;call()的参数 thisArg&#xff1a;在调用 func 时要使用的 this 值 arg1, …, argN &#xff08;可选&#xff09; 函数的参数 ✨call()的描述&#xff1a; 首先声明 func是一个函数&#xff0c;person是一个对象 针对这段代码&#xff1a;f…

Json“牵手”亚马逊商品详情数据方法,亚马逊商品详情API接口,亚马逊API申请指南

亚马逊平台是美国最大的一家网络电子商务公司&#xff0c;亚马逊公司是1995年成立&#xff0c;刚开始只做网上书籍售卖业务&#xff0c;后来扩展到了其他产品。现在已经是全世界商品品种最多的网上零售商和第二互联网公司&#xff0c;亚马逊是北美洲、欧洲等地区的主流购物平台…

【2023最新版】腾讯云CODING平台使用教程(Pycharm/命令:本地项目推送到CODING)

目录 一、CODING简介 网址 二、CODING使用 1. 创建项目 2. 创建代码仓库 三、PyCharm&#xff1a;本地项目推送到CODING 1. 管理远程 2. 提交 3. 推送 4. 结果 四、使用命令推送 1. 打开终端 2. 初始化 Git 仓库 3. 添加远程仓库 4. 添加文件到暂存区 5. 提交更…

OPENCV+QT环境配置

【qtopencv开发入门&#xff1a;4步搞定opencv环境配置2】https://www.bilibili.com/video/BV1f34y1v7t8?vd_source0aeb782d0b9c2e6b0e0cdea3e2121eba 第一步&#xff1a; 安装QT Qt 5.15 第二步&#xff1a; 安装OPENCV VS2022 Opencv4.5.5 C 配置_愿飞翔的鱼儿的博客…

认识doubbo和rpc

开个新坑&#xff0c;和大家一起学习Dubbo 3.X。我们按照一个由浅入深顺序来学习&#xff0c;先从使用Dubbo开始&#xff0c;再深入Dubbo的核心原理。 今天我们就从认识Dubbo开始&#xff0c;整体的内容可以分为3个部分&#xff1a; Dubbo是什么RPC是什么Dubbo的架构 正式开…