黄金2:行稳致远-如何让你的线程免于死锁
欢迎来到《并发王者课》,本文是该系列文章中的第12篇。
在上篇文章中,我们介绍了死锁的概念及其原因,本文将为你介绍的是几种常见的死锁预防策略。
简单来说,预防死锁主要有三种策略:
- 顺序化加锁;
- 给锁一个超时期限;
- 检测死锁。
一、顺序化加锁
通常,死锁的产生是由于多个线程无序请求资源造成的。资源是有限的,不可能同时满足所有线程的请求。然而,如果能按照一定的顺序分别满足各个线程的请求,那么死锁也就不再存在,也就是所谓的顺序化加锁(Lock Ordering)。
举个通俗点的例子,烦人的路口堵车你一定遇到过。路口之所以堵车,是因为车太多了,大家都争相往自己的方向去,互不相让,不堵才怪。这时候,就需要交警在中间进行协调指挥,疏解拥堵。交警之所以可疏解拥堵,其根本原因在于,交警让原本处于无序竞争的车流变成了井然有序的队列。
车还是那么多的车,路口还是那个路口,可是道路通畅了,这和线程的锁竞争是类似的道理。
在上篇文章的死锁代码中,线程1和线程2分别先占有了A和B,导致了死锁。按照刚才的思路,我们把顺序调整下,线程1和线程2都先占有A,然后再同时争夺B,那么死锁就不会发生。
定义哪吒线程1,先抢占A再争夺B:
private static class NeZha implements Runnable {public void run() {synchronized(lockA) {System.out.println("哪吒: 持有A!");try {Thread.sleep(10);} catch (InterruptedException ignored) {}System.out.println("哪吒: 等待B...");synchronized(lockB) {System.out.println("哪吒: 已经同时持有A和B...");}}}
}
定义兰陵王线程2,也是先抢占A再抢占B,这与此前就不同了:
private static class LanLingWang implements Runnable {public void run() {synchronized(lockA) {System.out.println("兰陵王: 持有A!");try {Thread.sleep(10);} catch (InterruptedException ignored) {}System.out.println("兰陵王: 等待B...");synchronized(lockB) {System.out.println("兰陵王: 已经同时持有A和B...");}}}
}
启动两个线程:
public class DeadLockDemo {public static final Object lockA = new Object();public static final Object lockB = new Object();public static void main(String args[]) {Thread thread1 = new Thread(new NeZha());Thread thread2 = new Thread(new LanLingWang());thread1.start();thread2.start();}
}
两个线程的输出结果如下:
哪吒: 持有A!
哪吒: 等待B...
哪吒: 已经同时持有A和B...
兰陵王: 持有A!
兰陵王: 等待B...
兰陵王: 已经同时持有A和B...
从运行的结果中可以看到,两个线程都先后获得了自己所需要的资源,而没有导致死锁。
调整加锁顺序是一种简单但有效的死锁预防策略。但是,这一策略并不是万能的,它仅适用于你在编码时已经知晓加锁的顺序。
二、给锁一个超时期限
在上篇文章中,我们说过死锁的产生有一些必要的条件,其中一个是无限等待。设定锁超时时间正是为了打破这一条件,让无限等待变成有限等待。
仍然以前面的代码为例,哪吒和兰陵王两个线程在争夺资源时,对方都互不相让导致了无限等待的僵局。而此时,如果其中任何一方给等待设定一个期限,那么时间一到,僵局将不攻自破,而线程仍可以再稍等片刻后继续尝试。
需要注意的是,synchronized
代码块不可以指定锁超时。所以,如果需要锁超时,你需要使用自定义锁,或者使用JDK提供的并发工具类。相关工具类的用法,会在后续文章中介绍,本文暂不展开描述。
另外,所谓给锁加一个超时的期限,其实有两层含义。一是在请求锁时需要设定超时时间,二是在获取锁之后对锁的持有也要有个超时时间,总不能到手就不放,那是耍流氓。
三、死锁检测
作为死锁预防的第三种策略,你可以认为死锁检测(Deadlock Detection)是一项较重的被动技能,当我们无法顺序化加锁,也无法设置锁的超时时间,那么就需要进行死锁检测。
死锁检测的核心原理在于对线程和资源进行数据化打标和跟踪。
在线程获取锁时,会将锁和线程的对应关系通过Graph或者Map等数据结构记录下来。这样一来,线程在获取锁被拒绝时,可以通过遍历已经记录的数据分析是否存在死锁。
当线程发现死锁的情况后,可以采取释放锁,稍等片刻后再次尝试。
附、如何可视化查看线程死锁等状态
在你感觉线程可能被阻塞或死锁时,可以通过jstack
命令查看。如果存在死锁,输出的结果中会有明确的死锁提示,如下面所示:
$ jstack -F 8321
Attaching to process ID 8321, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100
Deadlock Detection:Found one Java-level deadlock:
============================="Thread2":waiting to lock Monitor@0x000af398 (Object@0xf819aa10, a java/lang/String),which is held by "Thread1"
"Thread1":waiting to lock Monitor@0x000af400 (Object@0xf819aa48, a java/lang/String),which is held by "Thread2"Found a total of 1 deadlock.
除了jstack
之外,JProfiler也是一款非常强大的线程与堆栈分析工具,并可以和IDEA等IDE完美结合。
借助于JProfiler,我们可以非常直观地看到上述示例代码中的死锁,也可以在Thread Monitor中看到两个线程的状态为blocked.
需要注意的是,JProfiler是一款付费软件,它提供了十天的免费试用时间。如果没有常规的使用需求,而是仅用于学习的话,十天也是够用的。当然,你也可以考虑使用jConsole、jVisualvm等。
小结
以上就是关于死锁预防策略的全部内容。在本文中,我们介绍了三种死锁预发策略。三种策略各有利弊,就实际工作中的应用而言,第二种给锁设定超时期限是更为常用的一种做法,而第一种和第三种具有一定的逻辑难度和技术难度,更侧重于理解而非实际应用。
正文到此结束,恭喜你又上了一颗星✨
夫子的试炼
- 通过
jstack
命令查看死锁并解决。
延伸阅读与参考资料
- Deadlock Prevention
- 《并发王者课》大纲与更新进度总览
关于作者
关注公众号【庸人技术笑谈】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不做标题党。
如果本文对你有帮助,欢迎点赞、关注、监督,我们一起从青铜到王者。
关于作者
关注公众号【庸人技术笑谈】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不做标题党。
如果本文对你有帮助,欢迎点赞、关注、监督,我们一起从青铜到王者。
黄金2:行稳致远-如何让你的线程免于死锁相关推荐
- 萨摩耶数科林建明:坚守“终局思维” 让金融科技发展行稳致远
今天,随着我们启用"萨摩耶数科"这一全新的品牌,标志着"萨摩耶数科"正式迈入了以"科技"为发展核心的2.0时代.作为一家以AI为驱动的金融数 ...
- 从能用到好用,GIS信创如何做到行稳致远?
艾瑞咨询集团出品的<2021年中国信创产业研究报告>中提到,信创内涵体现在"从'关键环节.部分市场'走向'全产业链.全行业'的信息技术升级,构建中国自主的IT标准和生态" ...
- 行稳致远,进而有为——2020年 XAG发展展望
行稳致远,进而有为--2020年 XAG发展展望 一.XAG系统传承于著名的Ripple 二.XAG系统致力于构建具有普惠金融价值的自金融体系 三.XAG登陆vvcoin.com 四.开源 第一,上全 ...
- 出海2.0时代,中国企业如何行稳致远?
出海,如今已是中国企业的常态.哪怕面临疫情和贸易保护主义带来的种种不确定性,中国企业走出去的步伐依然愈发坚定. 今年中国贸促会研究院报告就显示,中国对外直接投资流量和存量连续四年稳居全球前三,近八成中 ...
- 【金猿信创展】恒生电子——全栈式信创解决方案,助力金融信创行稳致远
国产化/信创·恒生电子 本内容由恒生电子投递并参与"数据猿年度金猿策划活动--2022大数据产业国产化优秀代表厂商"评选. 数据智能产业创新服务媒体 --聚焦数智 · 改变商 ...
- 铜板街“行稳致远”的背后逻辑 | 一点财经
"应该尊重科技,尊重真正的互联网金融公司,这些公司现在都还活着,而且最后会剩下一两百家来服务整个中国市场." 在近日举行的全球金融科技盛会Money20/20中国大会上,何俊如此表 ...
- 如何让人形机器人“行稳致远”?这篇顶级期刊的论文提出了新方法
随着人形机器人行业的快速发展,其在现实生活中的应用也愈发变得可能.如何在适应不同的地面环境的同时,还能保证机器人的行走安全,是一个亟待解决的课题. 作者 | 优必选 优必选科技与代尔夫特理工大学丁加涛 ...
- 行稳致远!大数据基础设施“领航者”爱数的数智化进阶
<中智观察>第1740篇推送 作者:海比研究院 编辑:小宏 编审:杨小天 如今,互联网.大数据.云计算.人工智能.区块链等技术加速创新,日益融入经济社会发展中的各个领域,数智化正在成为催生 ...
- 工程项目管理数字化系统建设持续推动施工企业数字化转型行稳致远
目前铁路.公路工程企业工程项目数量多.分布范围广.项目的临时性和个性化,对现场安全管理信息化的建设,提出了更高要求.既要适应现场IT管理能力弱的事实,还要保证系统的构建和应用要简单易行:又要适应项目临 ...
最新文章
- QoS、IPv6、软交换和VoIP技术受质疑
- Convert.ToInt32()与int.Parse()的区别 (转载)
- 小程序入门到精通(三):学小程序必备技术基础-flex布局
- asp.net很有用的字符串操作类
- SQL数据库语言基础之SQL Server自带数据类型、自定义数据类型与使用、创建修改数据表
- 我是如何寻找数据集的,一些个人私藏
- python time模块计时_python中计时模块timeit的使用方法
- GPT-3:现实版的“贾维斯”?还是真“人工”智能?
- 大数据可视化有什么作用
- SQLServer2008 去除换行符
- jdk动态代理为什么只能为接口生成代理类?
- Nosql部署集群环境创建 Redis 集群管理集群
- 对破解软件的一点点理解
- 学位房如何查询学位真实性和户口是否被占用
- Line 167. parse error, unexpected IS, expecting SEMICOLON ISE14.7
- 怎么恢复苹果手机通讯录
- js动态渲染的页面发现鼠标悬浮hover事件失效
- 学习前端的实用网站——未完待续
- 幸运抽奖java_java实现幸运抽奖系统
- CPU使用率过高及优化方法