以下内容转自http://ifeve.com/deadlock-prevention/:

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

加锁顺序

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

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

Thread 1:lock A lock BThread 2:wait for Alock C (when A locked)Thread 3:wait for Await for Bwait for C

如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

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

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

加锁时限

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

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

Thread 1 locks A
Thread 2 locks BThread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blockedThread 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包下的工具。写一个自定义锁类不复杂,但超出了本文的内容。后续的Java并发系列会涵盖自定义锁的内容。

死锁检测

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

每当一个线程获得了锁,会在线程和锁相关的数据结构中(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)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

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

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

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

17、Java并发性和多线程-避免死锁相关推荐

  1. 并发基础篇(一): Java 并发性和多线程

    说在前面 介绍文章之前,先给出一个多线程的思维导图, 后续的文章就根据思维导图来一步一步的分析java多线程的知识. 一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多 ...

  2. 【转】JAVA 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)

    原文地址:https://www.cnblogs.com/edenpans/p/6020113.html 参考文章:http://ifeve.com/java-concurrency-thread-d ...

  3. Java并发性和多线程介绍

    作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...

  4. 5、Java并发性和多线程-相同线程

    以下内容转自http://tutorials.jenkov.com/java-concurrency/same-threading.html(使用谷歌翻译): 相同线程(同一线程)是一种并发模型,其中 ...

  5. 18、Java并发性和多线程-饥饿与公平

    以下内容转自http://ifeve.com/starvation-and-fairness/: 如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,这种状态被称之为"饥饿& ...

  6. java开源线程池_线程池 - Java 并发性和多线程 - UDN开源文档

    线程池 线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用.因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等. 我们可以把并发执行的任务传递给一个 ...

  7. 21、Java并发性和多线程-Java中的锁

    以下内容转自http://ifeve.com/locks/: 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的 ...

  8. 【Java并发性和多线程】Java中的锁

    2019独角兽企业重金招聘Python工程师标准>>> 本文为转载学习 原文链接:http://ifeve.com/locks/ 锁像synchronized同步块一样,是一种线程同 ...

  9. Java并发性和多线程介绍、优缺点

    在过去 单核CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并 不是真正意义上的"同一时间点",而是多个任务 ...

最新文章

  1. java获取焦点的组件_JAVA组件焦点的特性:获取组件时其顶层组件必须为可见的...
  2. Alpha多样性稀释曲线rarefraction curve还不会画吗?快看此文
  3. R语言实现sigmoid激活函数并可视化
  4. git cherry-pick 分支转移教程
  5. Apollo代码学习(六)—模型预测控制(MPC)_follow轻尘的博客-CSDN博客_mpc代码
  6. (七十)Xcode5及以上对于状态栏和导航栏样式的设定方法
  7. Velocity介绍
  8. ABAP日期函数应用
  9. 首批 8 款 5G 手机获 3C 认证;iPhone6 系列停产;Android Q Beta 5 发布 | 极客头条
  10. 剑指offer面试题63. 股票的最大利润(动态规划)
  11. vue+ 高德地图 热力图
  12. 【.NET IoT】把达特甲醛传感器DART WZ-S接到树莓派RaspberryPi 3 b+上
  13. Unity 解决安卓包激励视频广告退后台再进入APP广告消失
  14. 表格分组标签:表格行分组中的隐藏功能
  15. 把json数据导入linux,使用json文件给es中导入数据
  16. Python-3 EXCEL 操作-1
  17. 两个三维向量的夹角计算
  18. 局域网下 共享文件夹
  19. AI每日小练习之磨砂玻璃质感图标
  20. MATLAB强化学习实战(二) 使用并行计算训练倒立摆系统

热门文章

  1. 20210310 20210311 :栈、队列、堆类题目合集
  2. 硬币兑换python 每个面值有多个_【算法27】硬币面值组合问题
  3. python缩进问题idle_Python IDLE中的缩进问题
  4. 【转】VB代码VB小程序:宇宙大爆炸演示
  5. 光影之路 GPU架构发展史(3/4)
  6. Google开启全新图像目标检测大赛,场景多样性和复杂度史无前例
  7. 李飞飞CVPR最新论文 | 「文本转图」效果优化可多一步:物体关系描述
  8. kubernetes 应用快速入门
  9. django文件——django + jquery-file-upload上传篇(二)-- 插件实现文件上传+进度条显示 +拖入文件上传...
  10. Xcode pch文件配置及object c 单例创建