文档章节

【15】死锁的防范

秋雨霏霏
 秋雨霏霏
发布于 2017/09/09 22:42
字数 1665
阅读 45
收藏 0
点赞 0
评论 0

加锁顺序

多线程发生死锁,主要是和他们加锁的顺序有关系。

如果确定所有的锁总是按照相同的顺序加锁,那么就不会遇到死锁。 来看看这个例子:

Thread 1:

  lock A 
  lock B


Thread 2:

   wait for A
   lock C (when A locked)


Thread 3:

   wait for A
   wait for B
   wait for C

如果像线程3这样,需要好几个锁,那就需要注意这几个锁的顺序。

例如,如果线程1先拿到锁A时,那线程2和线程3都不会拿到锁C。 这个时候,线程2和线程3其实都只能等待线程1释放锁A才能继续去申请锁B和锁C。

加锁顺序是预防死锁的一种简单而有效的机制。 然而,这是在你知道需要用到的全部锁的情况时,才能起作用的。 但实际中可能不是这样的。所以这个方法也并不是万能的。

加锁超时

另一个预防死锁的方法,就是在申请锁的时候,设置一个超时时间。 这样,就可以防止线程在某个得不到的锁上永久等待。 在超时时间之后,如果线程还得不到锁,那线程就可以返回加锁失败。 这样就可以根据这个返回信息,有机会来处理一些逻辑。 针对死锁,当然就可以在返回加锁失败时,就可以释放掉之前已经获得的锁。 这样就避免了其他线程长时间等待这些锁的情况发生了。 如此,就可以让程序避免死锁。

下面这个例子,展示了两个线程对两个锁以不同的顺序进行加锁,但是这次他们带有超时设定:

Thread 1 获得 锁A
Thread 2 获得 锁B

Thread 1 尝试获取 锁B 失败,进入阻塞等待
Thread 2 尝试获取 锁A 失败,进入阻塞等待

Thread 1 在 锁B 等待超时
Thread 1 返回,并释放已获得的 锁A
Thread 1 随机休息一定时间(例如:257 毫秒)后,进行重试.

Thread 2 在 锁A 等待超时
Thread 2 返回,并释放已获得的 锁B
Thread 2 随机休息一定时间(例如:43 毫秒)后,进行重试.

在上面这个例子中,线程2将会先于线程1大约200毫秒进行重试。 这样线程2就会有机会顺利的拿到两个锁。 而线程1将会重新尝试获取锁A。 当线程2完成后,线程1也会重试,并有机会顺利拿到两个锁。

但要记住一点,锁等待超时时间的设定,不代表就一定发生死锁了。 这个超时时间是避免长时间在某个锁上阻塞而设定的,比如如果持有锁的线程需要执行一个非常耗时的任务,这种情况下设置超时时间也能有效的避免长时间等待的情况,也可以避免线程饥饿的情况发生。

另外,如果过多线程对同一个资源进行争用,设置超时时间,就可能导致线程会不断的一次又一次的重试。 比如两个线程下超时时间在0~500毫秒的范围就可以正常运行,但是这不代表10或者20个线程的情况下也能正常。 线程太多,资源争用频繁的情况下,就可能导致两个或者多个线程容易在同一时间点进行重试。

超时机制还会有一个问题,那就是,Java的synchronized锁,是无法设置超时时间的。 这种情况下,就需要使用并发包(JUC)下的Lock类,进行显式的锁控制。

侦测死锁

侦测死锁是一个重要的死锁预防机制。 特别是在无法确定加锁顺序以及无法设置超时时间的情况下。

每次线程获得一个锁时候,就把这个线程和锁的对应信息记录到某个容器中(如:map)。 另外,在每次线程请求锁的时候,也记录到这个容器中。

当线程请求锁失败时,线程可以遍历这个容器,来检测是否发生死锁了。 例如,如果线程A请求锁7,但是锁7已经被线程B拿到,那么线程A就可以检查线程B是不是也在请求线程A已经持有的锁。如果线程B确实也在请求线程A的锁,那就可以侦测到死锁了。

当然,实际中的情况远比两个线程的情况要复杂。 可能在多个线程之间循环依赖,导致一个复杂的死锁。

下面这个图,就展示了一个侦测4个线程死锁的情况:

image

那么,侦测到死锁后,线程又需要做些什么呢?

一种策略是,释放掉所有已经获得的锁,并随机休息一段时间后再次进行重试。 这个策略有点类似超时时间的处理方式。所以,同样的,在争用激烈的情况下,频繁的重试成功率也不会太高。

另一个更好的策略是,在线程设进行重试时,设置一个优先级。这样,让优先级低的某几个(或者一个)放弃执行,进入等待。而其他线程,就像没有发生死锁那样继续执行。这样,就可以避免重试再次失败的情况。

不过要注意,如果线程的优先级是固定的,那可能导致某些高优先级的线程一直得到处理,低优先级的线程一直得不到执行机会。为了避免这种情况的发生,可以在侦测到死锁的时候,随机分配优先级。


补充几点

避免死锁

  • 一次至多获得一个锁
  • 尽可能使用开放调用
  • 使用显示Lock类,替代内部锁机制
    • tryLock
    • timeout

有关加锁顺序的注意事项,也同样适用于数据库操作。 如,多个线程可能对某些数据执行update操作,这个时候,如果update的数据行,也是按不同顺序update,那也同样容易导致数据库死锁。

所以,在调用批量update时,最好对数据集合以相同的规则进行一次排序。

© 著作权归作者所有

共有 人打赏支持
秋雨霏霏
粉丝 143
博文 91
码字总数 160569
作品 0
杭州
CTO(技术副总裁)
程序员的自我修养——操作系统篇

目录: 1. 进程的有哪几种状态,状态转换图,及导致转换的事件。 2. 进程与线程的区别。 3. 进程通信的几种方式。 4. 线程同步几种方式。 5. 线程的实现方式. (用户线程与内核线程的区别) 6...

马浩
2014/06/30
0
0
3.15热话题:电商网银系统如何打击假冒钓鱼网站,提高在线信任!

  一年一度的3.15国际消费者权益日即将到来,产品质量、售后服务、虚假宣传等消费者权益保护问题再次成为社会关注焦点。然而,在互联网高速发展的今天,人们大部分消费行为都已经转移到互联...

sslor
2016/03/14
48
0
网络安全宣传

1 国家网络安全宣传周 http://120.37.140.134/youku/656F068EF5397994B1414EDA/03000801005569514A0C011BEE9F10D8253CF4-CE0D-E701-0F30-8BA99F82A6FB.mp4 2 360网络攻防实验室 http://113.1......

james_laughing
2016/09/20
8
0
WSFC资源死锁案例

之前在WSFC日志分析进阶篇中曾经提到过一些关于WSFC底层原理,例如Resource.dll,RHS,RCM,了解这些组件对于我们后期做群集排错有莫大的帮助,本文我们就通过一个实际的资源死锁的案例,来帮...

老收藏家
2017/10/20
0
0
高并发Java(5):JDK并发包1

在高并发Java(2):多线程基础中,我们已经初步提到了基本的线程同步操作。这次要提到的是在并发包中的同步控制工具。 1. 各种同步控制工具的使用 1.1 ReentrantLock ReentrantLock感觉上是...

卯金刀GG
2017/11/02
0
0
二层网络安全防范笔记

此文参考思科网络安全部分书箱所作笔记,当然此笔记只是二层网络安全防范里的一部分,需要根据自身的实际环境回以实施以保证网络安全。 MAC攻击与防范: 攻击:MAC Address泛洪攻击,可利用某...

arckyli
2017/09/20
0
0
性能优化(9)-Android ANR在线监控原理

主目录见:Android高级进阶知识(这是总目录索引) [This tutorial was written by 无心追求] Android中的Watchdog 在Android中,Watchdog是用来监测关键服务是否发生了死锁,如果发生了死锁...

ZJ_Rocky
2017/12/04
0
0
Android ANR在线监控原理

Android中的Watchdog 在Android中,Watchdog是用来监测关键服务是否发生了死锁,如果发生了死锁就kill进程,重启SystemServer Android的Watchdog是在SystemServer中进行初始化的,所以Watch...

无心追求
2017/12/02
0
0
多线程死锁的产生以及如何避免死锁

一、死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),...

宇你同在
07/08
0
0
mysql 锁机制 详解二 (死锁)

1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 2.3 剖析死锁的成因 6 3 总结 ...

LYQ1990
2016/05/19
55
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Xamarin Essentials教程地理定位Geolocation

Xamarin Essentials教程地理定位Geolocation 通过地理定位功能,应用程序可以获取用户的当前地理位置,如经纬度值。利用地理位置,可以在地图上定位,也可以转化物理位置,划分用户的归属地。...

大学霸
13分钟前
0
0
vue 编译警告 Compiled with 4 warnings

There are multiple modules with names that only differ in casing. This can lead to unexpected behavior when compiling on a filesystem with other case-semantic. Use equal casing.......

落雪飞声
17分钟前
0
0
开篇文章,长期记录安全情形

密码位置 密码位于注释中 密码位于服务器端文件中 通过访问根目录下.htaccess、robots.txt查看禁查路径 密码文件可能存在的路径:/、/extra/、/extras/ 密码加密 binary to base16 sha256 彩虹...

hirainn
30分钟前
0
0
mysql数据库设置root可以远程登录的方法

mysql数据库设置root可以远程登录的方法 Posted on 2018-02-21 21:08 sishuisufeng 阅读(161) 评论(0) 编辑 收藏 允许root用户在任何地方进行远程登录,并具有所有库任何操作权限,具体操作如...

rootliu
35分钟前
0
0
TensorFlow 图的基本操作

图的创建,一般只需要使用默认图就能满足大部分的需求了 # 1 创建图的方法# 在默认图中创建常量c = tf.constant(0.0)# 新建一个图g = tf.Graph()# 设置上下文管理器,标明操作...

阿豪boy
今天
0
0
git 忽略文件失效

git update-index --assume-unchanged */.project

林子大鸟
今天
1
0
实现验证码功能

1、实现验证码,并存储 import com.dtb.pc_enterprise.entity.EnterUserEntity;import com.dtb.pc_enterprise.service.AdminService;import com.dtb.pc_enterprise.util.RedisService;......

木九天
今天
0
0
iptables 实例

以下部分内容为网络查询并整理结果 filter表小案例 iptables规则五条链:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING 四个表:filter nat mangle raw ###netfilter和iptables说明: 1、 ne...

李超小牛子
今天
0
0
Java面试基础篇——第六篇:常见Map类的区别

常见的map类有: HashMap, ConcurrentHashMap (Jdk1.8) , LinkedHashMap, TreeMap, Hashtable。 其中我们最常用的莫过于HashMap, 和并发情况下使用的ConcurrentHashMap了,它们的主要区别就在...

developlee的潇洒人生
今天
2
0
spring-boot:run启动时,指定spring.profiles.active

Maven启动指定Profile通过-P,如mvn spring-boot:run -Ptest,但这是Maven的Profile。 如果要指定spring-boot的spring.profiles.active,则必须使用mvn spring-boot:run -Drun.profiles=test......

夜黑人模糊灬
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部