>>分享Java编程技术,对《Java面向对象编程》等书籍提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 26816 个阅读者 刷新本主题
 * 贴子主题:  三种用于避免死锁的技术 回复文章 点赞(0)  收藏  
作者:javathinker    发表时间:2016-11-03 20:37:01     消息  查看  搜索  好友  复制  引用

作者: shenzhang

在有些情况下死锁是可以避免的。本文将展示三种用于避免死锁的技术:

1.加锁顺序
2.加锁时限
3.死锁检测

加锁顺序

当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。

如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。看下面这个例子:

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)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

例如,线程2和线程3只有在获取了锁A之后才能尝试获取锁C(获取锁A是获取锁C的必要条件)。因为线程1已经拥有了锁A,所以线程2和3需要一直等到锁A被释放。然后在它们尝试对B或C加锁之前,必须成功地对A加了锁。

按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁(并对这些锁做适当的排序),但总有些时候是无法预知的。

加锁时限

另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行(加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。

以下是一个例子,展示了两个线程以不同的顺序尝试获取相同的两个锁,在发生超时后回退并重试的场景:

Thread 1 locks A
Thread 2 locks B
  
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
  
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
  
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.

在上面的例子中,线程2比线程1早200毫秒进行重试加锁,因此它可以先成功地获取到两个锁。这时,线程1尝试获取锁A并且处于等待状态。当线程2结束时,线程1也可以顺利的获得这两个锁(除非线程2或者其它线程在线程1成功获得两个锁之前又获得其中的一些锁)。

需要注意的是,由于存在锁的超时,所以我们不能认为这种场景就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

此外,如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种现象可能不会发生,但是如果是10个或20个线程情况就不同了。因为这些线程等待相等的重试时间的概率就高的多(或者非常接近以至于会出现问题)。 (超时和重试机制是为了避免在同一时间出现的竞争,但是当线程很多时,其中两个或多个线程的超时时间一样或者接近的可能性就会很大,因此就算出现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。)

这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你需要创建一个自定义锁,或使用Java5中java.util.concurrent包下的工具。写一个自定义锁类不复杂,但超出了本文的内容。

死锁检测

死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。

当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。

下面是一幅关于四个线程(A,B,C和D)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

那么当检测出死锁时,这些线程该做些什么呢?

一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁。

一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。




程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->Java注解
  JavaWeb开发-->JSP中使用JavaBean(Ⅱ)
  JSP与Hibernate开发-->持久化层的映射类型
  Java网络编程-->XML数据处理
  精通Spring-->Vue指令
  Vue3开发-->Vue组件开发基础
  实用类 EncodingDetect,查看一个文件的字符编码
  求素数
  详细介绍float和double类型的区别
  观察者模式和发布订阅模式的区别
  Java Optional 解决空指针异常总结
  编程语言搜索量排行:用十年数据告诉你什么最受欢迎
  Synchronized与ReentrantLock区别总结
  Java多线程volatile详解
  常用的正则表达式汇总
  正则表达式:运算符优先级
  Java入门实用代码:死锁及解决方法
  Java入门实用代码:获取本机IP地址及主机名
  Java入门实用代码:数组元素的反转
  中国有多少程序员?现在还值得学java吗?
  类加载中的双亲委派模型
  更多...
 IPIP: 已设置保密
楼主      
该用户目前不在线 patebeng11 
  
威望: 0
级别: 新手上路
魅力: 130
经验: 130
现金: 1044
发文章数: 7
注册时间: 0001-01-01
 消息  查看  搜索  好友  邮件  复制  引用


90后消防员张俊杰:退伍后回乡卖蜂蜜,年收入千万
干将莫邪为何用活人祭剑,薇娅偷逃税被罚13.41亿,李佳琦被浙江消保委点名!
网络主播薇娅因偷逃税款,被追缴税款、加收滞纳金并处罚款共计13.41亿元。
水泵
螺杆泵
全焊接球阀
焊接球阀
离心泵
离心水泵
球阀
球阀厂10
污泥螺杆泵13
上海螺杆泵20
螺杆泵厂14
上海离心泵
直埋全焊接球阀
埋地全焊接球阀
磁力泵
化工泵
上海球阀95
上海球阀厂151
帕特
Fully Welded Ball Valve
all Welded Ball Valve


螺杆泵
全焊接球阀
焊接球阀
球阀厂
离心泵
水泵

发文章时间 2021-12-24 17:23:48
 IPIP: 已设置保密 1 楼     
该用户目前不在线 kericnnoe 
威望: 未知
级别: 未知
魅力: 未知
经验: 未知
现金: 未知
发文章数: 未知
注册时间: 未知
 复制  引用


先從魔龍傳奇免費版下手,這才是真正的必勝之道
发文章时间 2022-12-06 01:54:36
 IPIP: 已设置保密 2 楼     
1页 2条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。