找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

[ JAVA开发技术 ] 【守望者 j2se】Java并发:隐藏的线程死锁

2014-10-12 13:21| 发布者: zhouy | 查看: 1914 | 收藏

摘要: 许多程序员都熟悉Java线程死锁的概念。死锁就是两个线程一直相互等待。这种情况通常是由同步或者锁的访问(读或写)不当造成的。Found one Java-level deadlock:============================="pool-1-thread-2": wait ...
许多程序员都熟悉Java线程死锁的概念。死锁就是两个线程一直相互等待。这种情况通常是由同步或者锁的访问(读或写)不当造成的。

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"


好消息是最新的JVM通常会帮你检测到这种死锁现象,但它真的做到了吗?最近一个死锁的问题影响了Oracle Service Bus的生产环境,这一消息使得我们不得不重新审视这一经典问题,并找出“隐藏”死锁存在的情况。本文将通过一个简单的Java程序向大家讲解一种非常特殊的锁顺序死锁问题,这种死锁在最新的JVM 1.7中并没有被检测到。


犯罪现场:通常,我习惯将出现严重Java并发问题的情况称之为犯罪现场,在这里你扮演一个侦查员的角色来解决问题。在这篇文章中,犯罪行为来源于客户端IT环境运行中断。你需要完成如下工作:

1.收集证据、线索和事实(线程转储,日志,业务影响,负载信息…)

2.审问目击证人、咨询相关领域专家(支撑团队,交付团队,供应商,客户…)



接下来的调查工作为:分析收集到的信息,并根据收集的证据建立一个或多个“嫌疑犯”名单。最终,将名单缩小到主要嫌犯或者说引发问题的根源者上。显然,“凡不能被证明有罪者均无罪”的条例在这里并不适用,这里用到的规则恰恰相反。缺少证据会妨碍你找到问题的根源。下一步你将会看到JVM对死锁检测的缺乏并不能说明你无法解决这一问题。


嫌疑犯:在解决该问题的过程中,“嫌疑犯”被定义为具有以下执行模式的应用程序或中间件代码:

1.在ReentrantLock写锁使用之后使用普通锁(执行线程#1)

2.在使用普通锁之后使用ReentrantLock 读锁(执行线程#2)

3.当前的程序由两个Java线程并发执行,但执行顺序与正常顺序相反

上面的锁排序死锁标准可以用下图表示:
 

现在我们通过Java实例程序说明这一问题,同时查看JVM线程转储输出。


Java实例程序


上面的死锁问题第一次是在Oracle OSB问题事例中发现的。之后,我们通过实例程序重建了该死锁。该程序只是简单的创建了两个线程,每个线程有不同的执行路径,并且以不同的顺序尝试获取共享对象的锁。我们还创建了一个死锁线程用来监控和记录。现在,下面的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 lock 
       private final Object sharedObject = new Object(); 
       // ReentrantReadWriteLock used for WRITE & READ locks 
       private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 
  
       /** 
        *  Execution pattern #1 
        */
       public void executeTask1() { 
  
             // 1. Attempt to acquire a ReentrantReadWriteLock READ lock 
             lock.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 lock 
             finally { 
                    lock.readLock().unlock(); 
             }            
  
             System.out.println("executeTask1() :: Work Done!"); 
       } 
  
       /** 
        *  Execution pattern #2 
        */
       public void executeTask2() { 
  
             // 1. Attempt to acquire a Flat lock 
             synchronized (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 lock 
                    finally { 
                           lock.writeLock().unlock(); 
                    } 
             } 
  
             System.out.println("executeTask2() :: Work Done!"); 
       } 
  
       public ReentrantReadWriteLock getReentrantReadWriteLock() { 
             return lock; 
       } 
}


一旦程序引起线程死锁,JVM虚拟机就会产生如下的线程转储输出。

 
死锁根源:ReetrantLock 读锁行为


我们发现在这一问题上主要和ReetrantLock读锁的使用有关。读锁通常不会被设计成具有所有权的概念(详细信息)。由于线程没有记录读锁,造成了HotSpot JVM死锁检测器的逻辑无法检测到涉及读锁的死锁。自发现该问题以后,JVM做了一些改进,但是我们发现JVM仍然不能检测到这种特殊场景下的死锁。现在,如果我们把程序中读锁替换成写锁,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跟踪,这点和普通锁相似。这就意味着JVM死锁检测器能够检测如下情况的死锁:


1.对象监视器上涉及到普通锁的死锁

2.和写锁相关的涉及到锁定的可同步的死锁

3.由于线程缺少对读锁的跟踪造成这种场景下JVM无法检测到死锁,这样增加了解决死锁问题的难度。

4.由于一些潜在的死锁会被忽略,在2005年人们再次提出是否有可能增加线程对读锁的跟踪。如果你遇到了涉及读锁的隐藏死锁,试试下面的建议:

5. 仔细分析线程调用的跟踪堆栈,它可以揭示一些代码可能获取读锁同时防止其他线程获取写锁

6.如果你是代码的拥有者,调用lock.getReadLockCount的方法跟踪读锁的计数



本文由守望者watchmen收集整理,部分内容源于网络(http://it.deepinmind.com)。本文仅代表作者个人观点,不代表守望者的本意。如有违法侵权内容,请提交到守望者管理员处,立即处理。

推荐阅读

【守望者  j2se】ConcurrentHashMap原理分析
【守望者 j2se】ConcurrentHashMap原
集合是编程中最常用的数据结构。而谈到并发,几乎总是离不开集合这类高级数据
【守望者  j2se】双向链表模拟
【守望者 j2se】双向链表模拟
我们熟悉了java单向链表的模拟,现在我就必须开始双向链表的模拟的.1.基础结构
【守望者 高并发】现有高并发WEB服务器 lighttpd Apache Nginx比较
【守望者 高并发】现有高并发WEB服务器
lighttpd网络服务器基于的Lighttpd的网络服务器具有这样的特点:占用内存资源
【守望者 高并发】C10K/C500K与I/O框架
【守望者 高并发】C10K/C500K与I/O框架
C10K、C/500K问题C10K 的意思是10000并发请求,C500K意思是500 000并发请求,
【守望者  j2se】虚拟机各部分内存溢出情况
【守望者 j2se】虚拟机各部分内存溢出
通过简单的小例子程序,演示java虚拟机各部分内存溢出情况:(1).java堆溢出:
【守望者  JMM】理解volatile内存语义
【守望者 JMM】理解volatile内存语义
理解volatile变量对写多线程程序还是很有帮助的,这样就会避免一上来就是syn这
【守望者 高并发】使用CAS实现高效并发处理
【守望者 高并发】使用CAS实现高效并发
守望者:在并发处理应用中,一般使用锁的方式来解决竞争问题,但锁的效率比较
【守望者  j2se】吃透 java I/O 工作机制-1
【守望者 j2se】吃透 java I/O 工作机
I/O 问题可以说是当今互联网 Web 应用中所面临的主要问题之一,因为当前在这
【守望者 大数据】Mahout学习路线图
【守望者 大数据】Mahout学习路线图
Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Z
【守望者 j2se】ConcurrentMap之putIfAbsent(key,value)用法讨论
【守望者 j2se】ConcurrentMap之putIfA
先看一段代码:public class Locale { private final static MapString, Lo
【守望者  javascript】判断IE浏览器世界上最短的代码
【守望者 javascript】判断IE浏览器世
最短的IE判定var ie=!-分析以前最短的IE判定借助于IE不支持垂直制表符的特性
【守望者 大数据】机器学习已成为大数据的基石
【守望者 大数据】机器学习已成为大数
机器学习(Machine Learning, ML)是一门多领域交叉学科,涉及概率论、统计学、
【守望者  j2se】多线程与并发知识点总结
【守望者 j2se】多线程与并发知识点总
对于多线程和并发编程这个比较大的技术模块,我们会整理一些帖子方便知识点的
【守望者  j2se】二叉树模拟
【守望者 j2se】二叉树模拟
接着我们就要写一个比较复杂的数据结构的,但是这个数据结构是很重要的,假如
【守望者 SRS  】SRS 源代码分析笔记(0.9.194)-分析服务器对端口的监听 ...
【守望者 SRS 】SRS 源代码分析笔记(
第一部分 分析服务器对端口的监听 端口监听与初始化(一)全局变量_srs_confi

行业聚焦  面试交流  职位推荐  开发视频   技术交流  腾讯微博  新浪微博

友情链接:课课家教育  阿里云  鲜果  W3Cfuns前端网  中国企业家  环球企业家  投资界  传媒梦工场  MSN中文网  Android开发者社区  cnbeta  投资中国网  又拍云存储  美通说传播  IT茶馆  网商在线  商业评论网  TechOrange  IT时代周刊  3W创新传媒  开源中国社区  二维工坊  Iconfans  推酷  智能电视网  FreeBuf黑客与极客  财经网  DoNews  凤凰财经  新财富  eoe移动开发者社区  i黑马  网易科技  新浪科技  搜狐IT  创业家  创业邦  腾讯财经  福布斯中文网  天下网商  TechWeb  雷锋网  新浪创业  和讯科技  品途O2O  极客公园  艾瑞网  抽屉新热榜  卖家网  人民网通信频道  拉勾网  创新派  简单云主机  

手机版|黑名单|守望者在线 在线教育 linux 高级程序设计 C/C++ 大数据 ( 蜀ICP备14029946号

成都守望者科技有限公司 © 2013-2016 All Rights Reserved