在这个简短的博客系列的最后BadTransferOperation中,我一直在讨论分析死锁,我将修复BadTransferOperation代码。 如果您看过本系列的其他博客 ,那么您将知道,为了达到这一点,我创建了死锁的演示代码,展示了如何掌握线程转储,然后分析了线程转储,弄清楚发生僵局的位置和方式。 为了节省空间,下面的讨论同时引用了本系列第1部分中的AccountDeadlockDemo类,其中包含完整的代码清单。

教科书中有关死锁的描述通常是这样的:“线程A将获得对象1的锁定,并等待对象2的锁定,而线程B将获得对象2的锁定,同时等待对象1的锁定”。 我以前的博客中显示的堆积,并在下面突出显示,是一个真实的死锁,其他线程,锁和对象陷入了直接,简单,理论上的死锁情况。

Found one Java-level deadlock:
=============================
'Thread-21':waiting to lock monitor 7f97118bd560 (object 7f3366f58, a threads.deadlock.Account),which is held by 'Thread-20'
'Thread-20':waiting to lock monitor 7f97118bc108 (object 7f3366e98, a threads.deadlock.Account),which is held by 'Thread-4'
'Thread-4':waiting to lock monitor 7f9711834360 (object 7f3366e80, a threads.deadlock.Account),which is held by 'Thread-7'
'Thread-7':waiting to lock monitor 7f97118b9708 (object 7f3366eb0, a threads.deadlock.Account),which is held by 'Thread-11'
'Thread-11':waiting to lock monitor 7f97118bd560 (object 7f3366f58, a threads.deadlock.Account),which is held by 'Thread-20'

如果将上面的文本和图像与以下代码相关联,则可以看到Thread-20已锁定其fromAccount对象( fromAccount ),正在等待锁定其toAccount对象(e98)

private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {synchronized (fromAccount) {synchronized (toAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}}

不幸的是,由于时序问题, Thread-20无法获得对对象e98的锁定,因为它正在等待Thread-4释放对该对象的锁定。 Thread-4无法释放锁,因为它正在等待Thread-7Thread-7正在等待Thread-11Thread-11正在等待Thread-20释放对对象f58的锁。 这个现实世界的僵局只是教科书描述的一个更复杂的版本。

这段代码的问题是,从下面的代码片段中,您可以看到我正在从Accounts数组中随机选择两个Account对象作为fromAccounttoAccount并将它们锁定。 由于fromAccounttoAccount可以引用accounts数组中的任何对象,这意味着它们以随机顺序被锁定。

Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));

因此, 解决方法是对Account对象的锁定方式施加顺序,并且只要顺序一致,任何顺序都可以执行。

private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {if (fromAccount.getNumber() > toAccount.getNumber()) {synchronized (fromAccount) {synchronized (toAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}} else {synchronized (toAccount) {synchronized (fromAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}}}

上面的代码显示了此修复程序。 在此代码中,我使用帐号来确保首先锁定具有最高帐号的Account对象,以便永远不会出现以上的死锁情况。

以下代码是此修复程序的完整列表:

public class AvoidsDeadlockDemo {private static final int NUM_ACCOUNTS = 10;private static final int NUM_THREADS = 20;private static final int NUM_ITERATIONS = 100000;private static final int MAX_COLUMNS = 60;static final Random rnd = new Random();List<Account> accounts = new ArrayList<Account>();public static void main(String args[]) {AvoidsDeadlockDemo demo = new AvoidsDeadlockDemo();demo.setUp();demo.run();}void setUp() {for (int i = 0; i < NUM_ACCOUNTS; i++) {Account account = new Account(i, rnd.nextInt(1000));accounts.add(account);}}void run() {for (int i = 0; i < NUM_THREADS; i++) {new BadTransferOperation(i).start();}}class BadTransferOperation extends Thread {int threadNum;BadTransferOperation(int threadNum) {this.threadNum = threadNum;}@Overridepublic void run() {for (int i = 0; i < NUM_ITERATIONS; i++) {Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));int amount = rnd.nextInt(1000);if (!toAccount.equals(fromAccount)) {try {transfer(fromAccount, toAccount, amount);System.out.print(".");} catch (OverdrawnException e) {System.out.print("-");}printNewLine(i);}}System.out.println("Thread Complete: " + threadNum);}private void printNewLine(int columnNumber) {if (columnNumber % MAX_COLUMNS == 0) {System.out.print("\n");}}/*** This is the crucial point here. The idea is that to avoid deadlock you need to ensure that threads can't try* to lock the same two accounts in the same order*/private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {if (fromAccount.getNumber() > toAccount.getNumber()) {synchronized (fromAccount) {synchronized (toAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}} else {synchronized (toAccount) {synchronized (fromAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}}}}
}

在我的示例代码,死锁的发生是因为时机问题,嵌套的synchronized在我的关键字BadTransferOperation类。 在此代码中, synchronized关键字位于相邻的行上; 但是,最后一点是,值得注意的是, synchronized关键字在代码中的什么位置都没关系(它们不必相邻)。 只要您使用同一线程锁定两个(或更多)不同的监视对象,就会发生排序和死锁。

有关更多信息,请参阅本系列中的其他博客 。

该系列以及其他博客的所有源代码都可以在Github上找到,网址为git://github.com/roghughe/captaindebug.git

参考: 调查死锁-第4部分:来自Captain Debug博客博客的JCG合作伙伴 Roger Hughes 修复代码 。

翻译自: https://www.javacodegeeks.com/2012/11/investigating-deadlocks-part-4-fixing-the-code.html

研究僵局–第4部分:修复代码相关推荐

  1. java 死锁的检测与修复_调查死锁–第4部分:修复代码

    java 死锁的检测与修复 在这个简短的博客系列的最后BadTransferOperation中,我一直在讨论分析死锁,我将修复BadTransferOperation代码. 如果您已经看过本系列的其 ...

  2. 污点修复代码_立即修复该代码!

    污点修复代码 您正在从事这个新项目,并且在某处看到一段不好的代码. 处理它的错误方法是"不,那是别人的代码,我对此没有做任何事情","我没有时间解决这个问题–我还有其他任 ...

  3. 我需要完全理解这部分代码才能确保它能够正常工作,如果由我来修复代码中的问题,我是不会这么写的,因此希望你也不要这么来写(转)...

    Jim Bird是一位经验丰富的软件开发经理.项目经理与CTO,专注于软件开发与维护.软件质量与安全等领域中疑难问题的解决.在过去的15年间,Jim曾管理过团队建设并主导过高性能的财务系统的建设.他的 ...

  4. 我用Transformer修复代码bug

    来源:机器之心 本文约3900字,建议阅读7分钟 本文介绍了一种预训练模型transformer进行自动debug的方法. 本地化 Bug 并修复程序是软件开发过程中的重要任务.在本篇论文中,来自微软 ...

  5. 打破冷漠僵局文章_研究僵局–第2部分

    打破冷漠僵局文章 调查死锁时最重要的要求之一就是要研究死锁. 在我的上一个博客中,我写了一些名为DeadlockDemo代码,该代码使用一堆线程在一系列银行账户之间转移随机数,然后陷入僵局. 该博客运 ...

  6. 中学再不学编程就晚了?MIT、JHU研究:程序员大脑思考代码的机制不对劲

    视学算法报道 转载自:机器之心 编辑:泽南.小舟 MIT.约翰霍普金斯大学的研究人员发现,思考编程并不像组织语言,但也不靠纯粹的逻辑.这是因为我们通常是在成年之后才开始学代码? 很多时候,我们认为优秀 ...

  7. 研究人员研发可自我修复的“电子皮肤”,重点是还能回收再利用

    研究者称,他们的"电子皮肤"不会产生任何废弃物. 今天,<科学进展>刊登了一篇研究论文,其中描述了一种可以完全回收的"电子皮肤",即使将其撕裂,也可 ...

  8. 从.Net框架Bug的提交到修复代码成功合并到.NET CoreFX主线

    从发现.NET Framework中SmtpClient的Bug并拿出解决方案,然后给微软开发者社区提交Bug开始,总共耗时一个多月,对Bug修复的代码最终被采纳,现已合并到.NET Core Lib ...

  9. 打破冷漠僵局文章_研究僵局–第3部分

    打破冷漠僵局文章 在本系列的前两个博客( 第1部分和第2部分)中 ,我演示了如何创建一段会死锁的错误代码,然后使用该代码演示进行线程转储的三种方式. 在此博客中,我将分析线程转储以找出错误的原因. 下 ...

最新文章

  1. python官网下载步骤linux-linux如何安装python
  2. C++:类中的赋值函数
  3. 全国大学生电工数学建模竞赛赛题_A
  4. php cookie 字串,php入门(字符串,cookie,session)
  5. 微软:求你们别再用 IE 浏览器了
  6. 容器编排技术 -- Kubernetes DNS Pod 与 Service 介绍
  7. c语言终极面试宝典 pdf,C语言终极面试--编程
  8. 在.net平台上运行伪JAVA
  9. linux审计日志清除,Linux登录安全及用户操作审计 ,linux下清理日志脚本
  10. 华为或者荣耀手机使用谷歌有Google弹窗该怎么办?
  11. PowerBI-时间智能函数-SAMEPERIODLASTYEAR
  12. Ubuntu上搭建Hadoop环境(单机模式+伪分布模式) - 狂奔的蜗牛 - 博客频道 - CSDN.NET http://blog.csdn.net/hitwengqi/article/detai
  13. DOOM之父约翰·卡马克
  14. Join the IT | 一个初生程序猿的内心独白
  15. 澳大利亚种植最辣辣椒 制作辣酱需穿防护服
  16. 微信小程序及微信生态圈
  17. Linuxserver沦陷为肉鸡的全过程实录
  18. 微信小程序开发消息推送配置教程
  19. POI导出设置复杂表头
  20. js中fn()和fn()()的区别

热门文章

  1. 网站能拿到其他网站的cookie_网站能给公司带来哪些好处?
  2. 水晶报表图形位置_看了我用Excel做的年度报表,老板直夸好
  3. JDK8的日期时间类3
  4. 三思笔记_使用反射前先三思
  5. kafka 消费端 api_在消费者的眼中:您真的需要为您的API提供客户端库吗?
  6. red hat 4.1.2_安装Red Hat Container Development Kit 2.2版本
  7. lucene_Lucene组件概述
  8. jboss调jvm参数_在同一台机器上启动多个JBoss A-MQ JVM
  9. java字节码_好的,每个接触Java字节码的人
  10. 如何在ADF中将参数传递给ActionListener