原文链接        作者:Jakob Jenkov

译者:申章   校对:丁一

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

加锁顺序

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

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

Thread 1:  lock A   lock BThread 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 AThread 2 locks BThread 1 attempts to lock B but is blockedThread 2 attempts to lock A but is blockedThread 1's lock attempt on B times outThread 1 backs up and releases A as wellThread 1 waits randomly (e.g. 257 millis) before retrying.Thread 2's lock attempt on A times outThread 2 backs up and releases B as wellThread 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)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

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

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

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


扩展阅读:译者注

  • 《现代操作系统 第三版》§6.2~§6.7

转载于:https://www.cnblogs.com/techfox/p/4501344.html

java学习避免死锁相关推荐

  1. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  2. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  3. Java学习4大阶段完成入门,小白必读!

    Java 的学习过程分为4个阶段: 理论阶段 开发阶段 进阶阶段 实战阶段 理论阶段 在具体谈论Java知识点之前,我想先跟同学们聊聊Java 语言自身的一些特点.生态系统以及适用的场景,这有助于我们 ...

  4. java学习笔记16--异常

    java学习笔记16--异常 异常 异常时导致程序中断运行的一种指令流,如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失, 所以在程序的设计中必须要考虑各种异常的发生,并正确的做 ...

  5. java 解决死锁_如何解决java中的死锁问题

    一.死锁的定义 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去. 那么我们换一个更加规范的定义:集合中的每一个进程都在 ...

  6. java学习之路目录(已完结)

    java学习之路目录(持续更新中-) 第一阶段 javaSE(完结) 序号 标题 内容 001 java初识 java语言特点.体系结构.运行机制 002 java SE基础语法 注释.关键字.变量. ...

  7. Java学习笔记22:并发(2)

    Java学习笔记22:并发(2) 图源:PHP中文网 终止任务 终止线程有一种非常简单的方式:设置一个多线程共享的标记位,子线程用轮询的方式检查这个标记位,如果该标记位显示取消状态,就让子线程退出执行 ...

  8. 十年架构师留下最完整的Java学习路线

    原文地址:https://club.dingding.xin/topic/5081/十年架构师留下最完整的java学习路线?code=cb850abbce523c7f886f583d6dc00f5e ...

  9. 十年架构师留下最完整的Java学习路线,学完年薪40W

    文章有点长,请大家耐心看完,话不多说直接上干货! 永不过时的编程语言--Java 编程开发. Java编程语言占比: 据官方数据统计,在全球编程语言工程师的数量上,Java编程语言以900万的程序员数 ...

最新文章

  1. 2022-2028年中国金属薄膜行业市场深度监测及投资潜力研究报告
  2. Code Push 热更新使用详细说明和教程
  3. [使用心得]maven2之m2eclipse使用手册之六使用Maven2插件创建一个简单的SSH2项目之jetty篇(一)...
  4. 基本农田卫星地图查询_发现谷歌地图替代网站,卫星地图街景功能都能用
  5. matlab打开矩阵表,如何将Excel数据导入MATLAB中?:EXCLE中通过矩阵表输出选项
  6. 如何修改maven默认仓库(即repository)的路径
  7. BZOJ1877 [SDOI2009]晨跑 【费用流】
  8. 服务器如何挂网页游戏,网页游戏怎么挂云服务器
  9. JavaScript:实现multiplesThreeAndFive三或五倍数的算法 (附完整源码)
  10. Python-opencv读取视频流处理后保存成mp4格式的视频源码
  11. FreeSWITCH的传真发送
  12. 3000字告诉你如何渡过程序员菜鸟时期
  13. pta简单的插入排序
  14. 网络能力认证CCSC-管理1级 技术1级别
  15. 鼠标移动事件(鼠标移动到主菜单上显示子菜单)
  16. 编写一个函数isprime(n).判断整数n是否为素数.编写程序使用此函数,当输入一个整数时,对它进行判断,当为素数时,输出1.否则,输出0....
  17. Java_B_Province测试次数
  18. 计算机专业知识文章,计算机专业知识翻译成英文,帮帮忙!
  19. Keras:Unet网络实现多类语义分割方式
  20. Win10 如何快速一键自动优化新装系统?

热门文章

  1. [深度学习-优化]dropout防止过拟合的理解
  2. python安装wxpython库_wxPython:python 首选的 GUI 库
  3. java 如何判定消息已在队列_【05期】消息队列中,如何保证消息的顺序性?
  4. c语言多线程_马云都称赞的C语言、C++学习路线!!!!
  5. 深度学习的实用层面 —— 1.9 正则化输入
  6. 知识图谱入门2-1:实践——基于医疗知识图谱的问答系统
  7. Font Configuration and Customization for Open Source Systems - 白皮书翻译和深入
  8. 广西二级c语言试题,广西区计算机等级考二级C语言笔试试题及答案.doc
  9. python如何给一组数据打标签_给数据集打标签python代码实现(1)
  10. adb最新版本是多少_新版本更新有问题?别慌,来这里找答案!