隐藏的线程死锁
有一些死锁可能是由于 flat (synchronized) 或 ReentrantLock (读或写) lock-ordering 锁顺序问题引起的,比如我们得到threaddump输出,就知道发生死锁。
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并不能发现死锁,这种死锁情况通常发生如下情况:
- FLAT锁后面跟着ReentrantLock WRITE 写锁 (execution path #1)
- ReentrantLock READ锁后面跟着FLAT锁 (execution path #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 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;
}
}
JVisualVM的threadDump输出:

JVM并没有发现死锁 (e.g. 没有报警:found one Java-level deadlock) ,但是很清楚这个两个线程陷入了死锁状态。
主要原因是ReetrantLock READ 锁的使用,read锁通常不会设计为一个所有权的概念,因为并没有记录去保存读锁。这就阻止JVM侦听包括读锁的死锁逻辑,如果我们使用写锁替代读锁,JVM就会发现死锁,这是因为写锁被JVM作为flat 锁一样跟踪.。
下面方式有助于排出隐藏的死锁:
- 密切线程调用堆栈跟踪分析,它可能揭示了一些代码,可能获取读锁定并防止其他线程获取写锁
- 如果你为自己写代码,用lock.getReadLockCount()方法读取锁定计数跟踪。
死锁详解研究
java多线程
Java同步或锁
Java性能调优