线程死锁——死锁产生的条件
什么是线程死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于相互等待状态,若无外力作用,它们将无法继续执行下去。
造成死锁的原因可以概括成三句话:
- 当前线程拥有其他线程需要的资源
- 当前线程等待其他线程已拥有的资源
- 都不放弃自己拥有的资源
线程死锁产生的四个必要条件
- 互斥,共享资源 X 和 Y 只能被一个线程占用;
- 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
- 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
- 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
举个必然产生死锁的例子
public static void main(String[] args) {Object a = new Object();Object b = new Object();// 线程1new Thread(() -> {synchronized (a) {System.out.println("获得了A锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (b) {}}}).start();// 线程2new 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,这样自然就避免了循环等待的情况发生。
总结
死锁是一个比较头疼的问题,但是只要我们的代码规范,可以避免大多数情况下的死锁。还有避免死锁的经典算法是银行家算法,这里就不扩开介绍了。
在很多情况下,尤其是多线程编程中,我们要注意线程之间的资源是否存在互相竞争的情况,如果有,要及时规避死锁的风险。
死锁很多时候会发生在数据库操作中,例如长事务、并发条件下的共享锁升级等都会造成数据库死锁,后面有时间会专门针对数据库死锁讲一讲。
线程死锁——死锁产生的条件相关推荐
- Linux 多线程(二)线程安全:线程安全、互斥与互斥锁、死锁、同步与条件变量
线程安全 互斥 死锁 同步 线程安全 所谓线程安全,其实就是当多个线程对临界资源进行争抢访问的时,不会造成数据二义或者逻辑混乱的情况(通常情况下对全局变量和静态变量进行操作时在会出现) 常见的线程安全 ...
- 关于操作系统中进程、线程、死锁、同步、进程间通信(IPC)的超详细详解整理
作者主页:https://www.zhihu.com/people/san-hao-bai-du-ren-79 一.什么是进程?什么是线程? 1.1 进程定义 1.2 线程定义 1.3 ...
- Java线程的死锁和活锁
文章目录 1.概览 2.死锁 2.1.什么是死锁 2.2 死锁举例 2.3 避免死锁 3.活锁 3.1 什么是活锁 3.2 活锁举例 3.3 避免活锁 1.概览 当多线程帮助我们提高应用性能的同时,它 ...
- 【操作系统学习笔记】—— 【二】进程、线程、死锁
本文参考: JavaGuide 王道考研-操作系统 CS-Notes 文章目录 一.进程的概念.组成.特征 1. 概念 2. 进程的组成 PCB 程序段 数据段 3. 进程的特征 二.进程的状态 三. ...
- 黄金2:行稳致远-如何让你的线程免于死锁
欢迎来到<并发王者课>,本文是该系列文章中的第12篇. 在上篇文章中,我们介绍了死锁的概念及其原因,本文将为你介绍的是几种常见的死锁预防策略. 简单来说,预防死锁主要有三种策略: 顺序化加 ...
- java线程饥饿死锁_java并发-线程饥饿死锁测试
线程饥饿死锁 <Java并发编程实践>中对线程饥饿死锁的解释是这样的:在使用线程池执行任务时,如果任务依赖于其他任务,那么就可能产生死锁问题.在单线程的Executor中,若果一个任务将另 ...
- linux线程(互斥锁、条件)
线程概念: 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情.有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务. ...
- 死锁——什么是死锁 死锁的四个必要条件 避免死锁
1 什么是死锁 死锁的定义是:在一个进程组内,每个进程都在等待只有其他进程才能引发的事件,那么该进程组处于死锁状态. 有两个线程(或者更多的线程),每个线程都在等待被其他线程占用的资源. 比如:线程A ...
- 分享:python,限制任意函数,线程的执行时间或根据条件终止.
python,限制任意函数,线程的执行时间或根据条件终止. http://my.oschina.net/u/1024140/blog/122778
最新文章
- 旷视MegEngine核心技术升级
- 网络安全界永恒不变的10大安全法则
- 【深度学习】Tensorflow搭建卷积神经网络实现情绪识别
- 《Python数据科学指南》——1.16 使用lambda创造匿名函数
- linux nvidia 361.run,Ubuntu 16.04安装nVidia驱动失败!
- BaseAdapter的ArrayIndexOutOfBoundsException
- 22. jQuery 遍历 - 同胞
- Echarts 下载使用教程
- tspline工具_TSpline2.0海豚建模教程.pdf
- iPhone X 不充电维修案例
- 数据结构——“双向循环链表“ 易懂刨析双向循环链表(图解+代码)
- php批量邮件地址,PHP通过phpmailer批量发送邮件功能
- ckfinder 配置 php,CKEditor4+CKFinder3(php版本)安装及配置方法
- 【SEED Labs 2.0】Packet Sniffing and Spoofing Lab
- latex tex studio 表格大小 整体缩小 过长 过宽 跨栏 跨页
- Data()笔记之getDay()的基本用法
- 1.C语言变量类型、全局变量、局部变量
- 通过改变电脑的某些设置来呵护我们的眼睛
- 《实时控制软件设计》第一次编程作业
- 雌雄异体,伴X染色体的等位基因(2个),计算子代的基因型频率
热门文章
- layui使用tips_layui的tips层怎么用?
- TX2进入Recover恢复模式
- 百亿级图数据在快手安全情报的应用与挑战
- 海航exchange邮箱服务器,Exchange 邮件系统
- MP3文件信息编辑利器 - Mp3tag
- springboot整合shiro(超详细,你想要的都在这了)
- setpercision(n) setiosflags(ios::fixed) setiosflags(ios::scientific)
- 计算机怎么填接新的网络,新电脑怎么设置网络连接
- DatabaseMetaData查询集群下的库名
- html做静夜思加css样式,HTML+CSS网页设计_图文.ppt