Java并发:隐藏线程死锁

大多数Java程序员熟悉Java线程死锁概念。 它本质上涉及2个线程,它们彼此永远等待。 这种情况通常是平面(同步)或ReentrantLock(读或写)锁排序问题的结果。

Found one Java-level deadlock:
=============================
"pool-1-thread-2":waiting to lock monitor 0x0237ada4 (object 0x272200e8, a java.lang.Object),which is held by "pool-1-thread-1"
"pool-1-thread-1":waiting to lock monitor 0x0237aa64 (object 0x272200f0, a java.lang.Object),which is held by "pool-1-thread-2"

好消息是,HotSpot JVM始终可以为您检测到这种情况……还是吗? 最近一个影响Oracle Service Bus生产环境的线程死锁问题迫使我们重新审视此经典问题并确定是否存在“隐藏”死锁情况。 本文将通过一个简单的Java程序演示并复制非常特殊的锁排序死锁条件,最新的HotSpot JVM 1.7并未检测到该情况。 您还可以在本文结尾处找到一个视频 ,向您介绍Java示例程序和所使用的故障排除方法。

犯罪现场

我通常喜欢将主要的Java并发问题与犯罪现场进行比较,在犯罪现场您扮演首席调查员的角色。 在这种情况下,“犯罪”是客户IT环境的实际生产中断。 您的工作是:

  • 收集所有证据,提示和事实(线程转储,日志,业务影响,负载数字…)
  • 询问证人和领域专家(支持团队,交付团队,供应商,客户……)

调查的下一步是分析收集的信息,并建立一个或多个“可疑”的潜在清单以及清晰的证据。 最终,您希望将其范围缩小到主要可疑或根本原因。 显然,“直到证明有罪之前无罪”的法律在这里并不适用,恰恰相反。 缺乏证据可能会阻止您实现上述目标。 接下来,您将看到Hotspot JVM缺少死锁检测并没有必要证明您没有在处理此问题。

犯罪嫌疑人

在此故障排除上下文中,“可疑”定义为具有以下有问题的执行模式的应用程序或中间件代码。

  • 使用FLAT锁,然后使用ReentrantLock WRITE锁(执行路径#1)
  • 使用ReentrantLock READ锁,然后使用FLAT锁(执行路径#2)
  • 由2个Java线程并发执行,但执行顺序相反

上面的锁排序死锁标准可以如下所示:

现在,让我们通过示例Java程序来复制此问题,并查看JVM线程转储输出。

示例Java程序

上面的死锁条件是首先从我们的Oracle OSB问题案例中发现的。 然后,我们通过一个简单的Java程序重新创建了它。 您可以在此处 下载我们程序的完整源代码。 该程序只是创建并触发2个工作线程。 它们每个执行不同的执行路径,并尝试以不同的顺序获取共享对象上的锁。 我们还创建了一个死锁检测器线程以进行监视和记录。 现在,在下面找到实现2条不同执行路径的Java类。

package org.ph.javaee.training8;import java.util.concurrent.locks.ReentrantReadWriteLock;/*** A simple thread task representation* @author Pierre-Hugues Charbonneau**/
public class Task {// Object used for FLAT lockprivate final Object sharedObject = new Object();// ReentrantReadWriteLock used for WRITE & READ locksprivate final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();/***  Execution pattern #1*/public void executeTask1() {// 1. Attempt to acquire a ReentrantReadWriteLock READ locklock.readLock().lock();// Wait 2 seconds to simulate some work...try { Thread.sleep(2000);}catch (Throwable any) {}try {              // 2. Attempt to acquire a Flat lock...synchronized (sharedObject) {}}// Remove the READ lockfinally {lock.readLock().unlock();}           System.out.println("executeTask1() :: Work Done!");}/***  Execution pattern #2*/public void executeTask2() {// 1. Attempt to acquire a Flat locksynchronized (sharedObject) {                 // Wait 2 seconds to simulate some work...try { Thread.sleep(2000);}catch (Throwable any) {}// 2. Attempt to acquire a WRITE lock                   lock.writeLock().lock();try {// Do nothing}// Remove the WRITE lockfinally {lock.writeLock().unlock();}}System.out.println("executeTask2() :: Work Done!");}public ReentrantReadWriteLock getReentrantReadWriteLock() {return lock;}
}

一旦触发死锁情况,就会使用JVisualVM生成JVM线程转储。

从Java线程转储示例中可以看到。 JVM未检测到此死锁条件(例如,不存在“发现一个Java级死锁”),但很明显,这两个线程处于死锁状态。

根本原因:ReetrantLock READ锁定行为

至此,我们发现的主要解释与ReetrantLock READ锁的用法有关。 读锁通常不设计为具有所有权概念。 由于没有哪个线程持有读取锁的记录,因此这似乎可以防止HotSpot JVM死锁检测器逻辑检测到涉及读锁的死锁。 从那时起,我们进行了一些改进,但是我们可以看到JVM仍然无法检测到这种特殊的死锁情况。 现在,如果我们用写锁替换程序中的读锁(执行模式2),那么JVM将最终检测到死锁情况,但是为什么呢?

Found one Java-level deadlock:
=============================
"pool-1-thread-2":waiting for ownable synchronizer 0x272239c0, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),which is held by "pool-1-thread-1"
"pool-1-thread-1":waiting to lock monitor 0x025cad3c (object 0x272236d0, a java.lang.Object),which is held by "pool-1-thread-2"Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x272239c0> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
acquireQueued(AbstractQueuedSynchronizer.java:867)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
acquire(AbstractQueuedSynchronizer.java:1197)at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:945)at org.ph.javaee.training8.Task.executeTask2(Task.java:54)- locked <0x272236d0> (a java.lang.Object)at org.ph.javaee.training8.WorkerThread2.run(WorkerThread2.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
"pool-1-thread-1":at org.ph.javaee.training8.Task.executeTask1(Task.java:31)- waiting to lock <0x272236d0> (a java.lang.Object)at org.ph.javaee.training8.WorkerThread1.run(WorkerThread1.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)

这是因为类似于平面锁,JVM跟踪写入锁。 这意味着HotSpot JVM死锁检测器似乎当前被设计用来检测:

  • 对象监视器上涉及FLAT锁的死锁
  • 死锁涉及与WRITE锁相关联的已锁定拥有的同步器

缺少读取锁每线程跟踪似乎可以防止这种情况下的死锁检测,并大大增加了故障排除的复杂性。 我建议您阅读Doug Lea在整个问题上的评论 ,因为早在2005年就开始担心由于某些潜在的锁定开销而可能添加每线程读取保持跟踪。 如果您怀疑涉及读取锁的隐藏死锁情况,请在下面的故障排除建议中查找:

  • 仔细分析线程调用堆栈跟踪,它可能会揭示某些代码潜在地获取读锁,并阻止其他线程获取写锁。
  • 如果您是代码的所有者,请通过使用lock.getReadLockCount()方法来跟踪读取锁的计数。

我期待着您的反馈,特别是对于具有此类涉及读锁的死锁经验的个人。 最后,在下面的视频中找到通过示例Java程序的执行和监视来解释这些发现的视频。


参考: Java并发性: Java EE支持模式和Java教程博客中,我们的JCG合作伙伴 Pierre-Hugues Charbonneau 隐藏的线程死锁 。

翻译自: https://www.javacodegeeks.com/2013/02/java-concurrency-the-hidden-thread-deadlocks.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369134.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue中使用axios发送请求

我们知道&#xff0c;vue2.0以后&#xff0c;vue就不再对vue-resource进行更新&#xff0c;而是推荐axios&#xff0c;而大型项目都会使用 Vuex 来管理数据&#xff0c;所以这篇博客将结合两者来发送请求 1.安装axios cnpm i axios -S 2.方案一&#xff1a;修改原型链 首先&…

django缓存

由于Django是动态网站&#xff0c;所有每次请求均会去数据进行相应的操作&#xff0c;当程序访问量大时&#xff0c;耗时必然会更加明显&#xff0c;最简单解决方式是使用&#xff1a;缓存&#xff0c;缓存将一个某个views的返回值保存至内存或者memcache中&#xff0c;5分钟内…

linux 输入法成繁体字_寻找Ubuntu中繁体字输入法

当客户来自港台地区时&#xff0c;英文和繁体字就成了交流的主要工具。windows下我们有搜狗输入法可以切换简体与繁体&#xff0c;那么Ubuntu下怎么办&#xff1f;這是我第一次考慮這個問題&#xff0c;在我的印象裏Linux下的中文輸入法還不是那麼完善&#xff0c;所以我進行了…

vue跨域解决方法

vue跨域解决方法 vue项目中&#xff0c;前端与后台进行数据请求或者提交的时候&#xff0c;如果后台没有设置跨域&#xff0c;前端本地调试代码的时候就会报“No Access-Control-Allow-Origin header is present on the requested resource.” 这种跨域错误。 要想本地正常的调…

CSS盒子模型之详解

前言&#xff1a; 盒子模型是css中最核心的基础知识&#xff0c;理解了这个重要的概念才能更好的排版&#xff0c;进行页面布局。一、css盒子模型概念 CSS盒子模型 又称框模型 (Box Model) &#xff0c;包含了元素内容&#xff08;content&#xff09;、内边距&#…

LeetCode的二分查找的练习部分总结

这两天由于工作的原因&#xff0c;一直没有写博客&#xff0c;但是却把LeetCode上面的题目做了不少——二分查找。上面这些题都是这两天写的。现在简单做一个总结。 首先二分查找的思想就是对一个有规律的元素&#xff08;事情&#xff09;进行不断的排除&#xff0c;最后找到符…

在Mac上安装IntelliJ IDEA

在Mac上安装IntelliJ IDEA http://www.jetbrains.com/idea/documentation/ 入门视频 这篇文章旨在介绍如何在Mac系统上安装IntelliJ IDEA&#xff0c;至于IntelliJ IDEA的介绍和使用方法&#xff0c;大家另行查阅&#xff0c;本篇的文章不再详细阐述。 简短解说&#xff0c;I…

行内元素,块级元素,各自特点及其相互转化

作为一名小前端&#xff0c;块级元素、行内元素用了几千几万次&#xff0c;除了“块级独占一行&#xff0c;行内不独占”之外&#xff0c;对细节属性的了解十分匮乏&#xff0c;今天做以部分属性的测试和阐述。 一、 对物理属性的支持 元素类别widthheightpaddingmargin是否独…

从RSS Feed和YQL创建数据表

Yahoo Query Language&#xff08; YQL &#xff09;是一种查询语言&#xff0c;例如SQL。 使用YQL&#xff0c;我们可以跨Web服务 查询 &#xff0c; 过滤和联接数据。 YQL也可以阅读RSS feed。 响应可以是JSON或XML。 雅虎提供了一个YQL控制台&#xff0c;用于调试 &…

Qt之QSS(Q_PROPERTY-自定义属性)

版权声明&#xff1a;进步始于交流&#xff0c;收获源于分享&#xff01;纯正开源之美&#xff0c;有趣、好玩、靠谱。。。作者&#xff1a;一去丶二三里 博客地址&#xff1a;http://blog.csdn.net/liang19890820 目录(?)[] 简述 在Qt之QSS&#xff08;Q_PROPERTY-原始属性&a…

python print error 空_python笔记37:10分钟掌握异常处理,再也不担心程序挂了

主要内容&#xff1a;小目标&#xff1a;异常处理主要内容&#xff1a;错误与异常&#xff0c;try_except语句对于撸代码的程序员来说&#xff0c;程序运行中出现问题是常见的现象&#xff1b;实际学习与工作中&#xff0c;我们会花很大的精力去解决各种问题&#xff1b;1. 程序…

contenteditable元素的placeholder输入提示语设置

在某些情况下&#xff0c;textarea是不够用的&#xff0c;我们还需要显示一些图标或者高亮元素&#xff0c;这就需要用富文本编辑器&#xff0c;而富文本编辑器本质上是HTML元素设置了contenteditable。 然后可能需要像input、textarea有placeholder的输入提示语&#xff0c;但…

css 浮动和清除浮动

在写页面布局的过程中&#xff0c;浮动是大家经常用的属性。在好多的排版布局中都是用的的浮动比如说下面这些地方都是应用到了浮动。 在我学习浮动的时候可是熬坏了脑筋&#xff0c;在这里我分享一下我对浮动这块知识的总结。 一、浮动的定义 使元素脱离文档流&#xff0c;按…

Java Code Geeks和Packt提供的Hadoop书籍赠品

亲爱的极客&#xff0c;由于参与度很高&#xff0c;并且为了有机会赢得尽可能多的Hadoop粉丝&#xff0c;我们决定将竞赛延长一个星期&#xff0c;直到下周二。 各位极客&#xff0c; 赠品在Java Code Geeks上继续。 我们很高兴地宣布&#xff0c;我们再次与Packt Publishin…

【转】 差分约束系统详解(转化为最短路) (概念)

---恢复内容开始--- 转自&#xff1a;http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 差分约束系统中&#xff1a; 如果求未知数的最大值&#xff0c;那么按小于等于建图后求最短路即可。&#xff08;因为求最短路是由无穷向下约束而得到的&#xff0c;所以得到…

【HTML基础】表格和表单

本次博客的主要内容如下&#xff1a; meta和link表格表单 meta和link meta meta的属性有两种&#xff1a;name和http-equiv。 name属性主要用于描述网页内容&#xff0c;对应与网页内容。 1.关键字&#xff0c;当搜索引擎在爬取内容的时候&#xff0c;会根据关键字判断&a…

python json 不好用_Python之json使用

一、概念json是一种通用的数据类型&#xff0c;任何语言都认识接口返回的数据类型都是json长得像字典&#xff0c;形式也是k-v { }其实json是字符串字符串不能用key、value来取值&#xff0c;要先转成字典才可以格式如下&#xff1a;{"error_code": 0,#要使用双引号&…

jstack命令(Java Stack Trace)

转&#xff1a;http://blog.csdn.net/fenglibing/article/details/6411940 JDK内置工具使用 一、javah命令(C Header and Stub File Generator) 二、jps命令(Java Virtual Machine Process Status Tool) 三、jstack命令(Java Stack Trace) 四、jstat命令(Java Virtual Machine …

链式存储mysql_链栈:栈的链式存储结构

前面讲完了栈的顺序存储结构&#xff0c;我们现在来看看栈的链式存储结构&#xff0c;简称为链栈。链栈是没有附加头结点的运算受限的单链表。栈顶指针就是链表的头指针。栈是用栈顶来做插入和删除操作&#xff0c;那么对于链栈的栈顶放在链表的头部还是尾部呢&#xff1f;单链…

树状数组 学习笔记

树状数组可以用来求区间元素的和。与前缀和做法不同&#xff0c;它支持值的修改。比如说&#xff0c;现在我有一个数列a&#xff0c;要求你维护这个数列&#xff0c;使其支持两个操作。1.改变数列第k项的值2.查询从第i项到第j项的总值暴力做法总是过不了所有点的&#xff0c;如…