关于线程死锁,我准备了一份“大餐”送给你

news/2024/5/20 11:36:19 标签: 多线程, java, 死锁

大家好,我是程序员青戈,今天带来一篇关于线程死锁的粗浅的介绍,希望能帮助到大家。

什么是线程死锁

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于相互等待状态,若无外力作用,它们将无法继续执行下去。

造成死锁的原因可以概括成三句话:

  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 不放弃自己拥有的资源

线程死锁产生的四个必要条件

  1. 互斥,共享资源 X 和 Y 只能被一个线程占用;
  2. 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
  3. 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
  4. 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。

举个必然产生死锁的例子

java">public static void main(String[] args) {
	   Object a = new Object();
	   Object b = new Object();
	
	   // 线程1
	   new Thread(() -> {
	      synchronized (a) {
	          System.out.println("获得了A锁");
	          try {
	              Thread.sleep(1000);
	          } catch (InterruptedException e) {
	              e.printStackTrace();
	          }
	          synchronized (b) {
	
	          }
	      }
	   }).start();
	   
	   // 线程2
	   new Thread(() -> {
	       synchronized (b) {
	           System.out.println("获得了B锁");
	           try {
	               Thread.sleep(1000);
	           } catch (InterruptedException e) {
	               e.printStackTrace();
	           }
	           synchronized (a) {
	
	           }
	       }
	   }).start();
}

上面的程序就是一个典型死锁的例子,为了保证死锁发生的几率,我这里在获得锁之后睡眠了1s。

线程1在获得A对象锁之后等了1s去尝试获取B对象锁,这时线程1是持有A对象锁的;线程2在获得B对象锁之后等待1s去尝试获得A对象锁,这时线程2是持有B对象锁的;就在它们彼此想获得对方的锁的时候,死锁发生了,并且一直持续下去。

如何避免死锁

上面提到只有这四个条件都发生时才会出现死锁,那么意思就是说,只要我们破坏其中一个,就可以成功预防死锁的发生。

  • 破坏互斥:只有一把锁,这是形成死锁的最关键的原因。显然,如果我们能在两个线程跑之前,能给每个线程单独拷贝一份钥匙的副本,就能有效的避免死锁了。

  • 占用且等待一次性申请所有的资源,这样就不存在等待了。
    例如线程1一次性拿到A和B两个锁,线程2在获取锁的时候需要等待线程1释放锁,这样就避免了多线程互相占用等待的情况。

  • 不可抢占:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
    在上面的死锁代码中,我们使用了synchronized关键字,它是不能主动释放资源的,会造成线程一直阻塞,JUC提供了Lock解决这个问题。
    显式使用Lock类中的定时tryLock功能来代替内置锁机制,可以检测死锁和从死锁中恢复过来。显式锁可以指定一个超时时限(Timeout),在等待超过该时间后tryLock就会返回一个失败信息,释放其拥有的资源,其他线程可以获取此资源避免死锁

  • 循环等待:如果一个线程需要一些锁,那么它必须按照确定的顺序获取锁。只有先获得了从顺序上排在前面的锁之后,才能获取后面的锁。
    破坏循环条件很简单,只要线程之间不要出现交叉占用的情况即可,也就是说在在代码中尽量避免线程1保持A请求B,线程2保持B请求A,尽可能使他们请求的顺序一致,比如线程1请求的顺序是A、B,线程2请求的顺序也是A、B,这样自然就避免了循环等待的情况发生。

总结

死锁是一个比较头疼的问题,但是只要我们的代码规范,可以避免大多数情况下的死锁。还有避免死锁的经典算法是银行家算法,这里就不扩开介绍了。

在很多情况下,尤其是多线程编程中,我们要注意线程之间的资源是否存在互相竞争的情况,如果有,要及时规避死锁的风险。

死锁很多时候会发生在数据库操作中,例如长事务、并发条件下的共享锁升级等都会造成数据库死锁,后面有时间会专门针对数据库死锁讲一讲。

本期分享到这里就结束了,对死锁问题感兴趣或者受此困扰的朋友可以在评论区交流哦😊

同时也欢迎您关注我的原创公众号:Java学习指南,私聊我进Java学习群一起交流、互助。

在这里插入图片描述
感谢阅读,下期见~


http://www.niftyadmin.cn/n/887769.html

相关文章

SpringBoot集成Mybatis保姆级教程(完整版)

文章目录前言项目搭建项目配置pom配置创建mapper文件夹数据源和mybatis配置启动类配置Mapper接口和XML自动生成测试啰嗦几句前言 大家好,我是程序员青戈。今天是周末,在家里想出一个教学视频,这个视频的内容是关于SpringBoot搭建Web应用的&a…

dede SQL语句调取文章链接

先上一段代码:思路:北京//一级栏目北京新闻同城活动天津//一级栏目天津新闻同城活动我要在每个子栏目,都要调用“同城活动”下的新闻,但是,需要在北京栏目下调用北京的同城活动,天津下调用天津的同城活动&a…

div显示在object、embed之上~

最近做一个项目时,发现浮动的div总是被object里的flash文件给盖住了。。本以为只是z-index的问题,于是去修改div和object、embed的z-index值~在改之前突然想到div是jqueryui里的dialog创建的~也就是说div本身默认的z-index已经是1000了,这个情况下应该说…

ecshop后台管理员忘记密码了怎么找回

代码如下 复制代码 <?php define(IN_ECS, true); require(dirname(__FILE__) . /includes/init.php); $userxiaoyao; //管理员用户名 $newpassx111111; //设置你的新密码 $sql"Select ec_salt FROM ". $ecs->table(admin_user) .&q…

db2 创建function错误 sqlcode=-104

求助:我下面的函数语法有问题吗?为甚么会一直报下面的错误 create function fun_fw_sfyczy(pi_operunitid varchar(2)) returns varchar(2) LANGUAGE SQL BEGIN ATOMIC DECLARE v_count int; set v_count (select count(1) from fw_operator where operunitid i…

【微信开发】上传下载多媒体文件

最近一段时间&#xff0c;开始入手了微信开发。感觉挺有意思的&#xff0c;虽然目前还是遇到了许多解决不了的问题。上传下载多媒体文件&#xff0c;这个作为高级接口的一部分功能&#xff0c;可能使用的人并不多&#xff0c;所以现在网上关于这个接口的使用教程很少。于是我就…

不知名二本应届生的一段求职经历

本人计科某不知名二本学校,大学方向是嵌入式,在此记录下自己大四秋招和春招的过程. 先说一下本人的基本情况,专业计科(嵌入式).由于对于嵌入式不是很感冒,所以除了完成培养计划的内容之后就没自己深入学习. 大三自己自学java,从最基础的javase部分到javaweb,在慢慢到学习一些…

企业QQ 增加在线交谈链接

企业QQ的在线交流链接跟普通QQ的在线交流不一样&#xff0c;普通QQ的在线交流&#xff0c;可以在http://shang.qq.com/v3/widget.html生成&#xff1b;企业qq的链接可以按以下步骤添加&#xff1a;第一步&#xff1a;引入企业QQjs脚本&#xff1a;<script charset"utf-…