扰应用程序的计时 ,从而改变死锁的可能性。)
清单 5. ReentrantLock 的调试版本会在检测到死锁时抛出 AssertionError
public class DebuggingLock extends ReentrantLock {
private static ConcurrentMap<Thread, DebuggingLock> waitingFor
= new ConcurrentHashMap<Thread, DebuggingLock> ();
public DebuggingLock() { super(); }
public DebuggingLock(boolean fair) { super(fair); }
private void checkDeadlock() {
Thread currentThread = Thread.currentThread();
Thread t = currentThread;
while (true) {
DebuggingLock lock = waitingFor.get(t);
if (lock == null || !lock.isLocked())
return;
else {
t = lock.getOwner();
if (t == currentThread)
throw new AssertionError("Deadlock detected");
}
}
}
public void lock() {
if (tryLock())
return;
else {
waitingFor.put(Thread.currentThread(), this);
try {
checkDeadlock();
super.lock();
}
finally {
waitingFor.remove(Thread.currentThread());
}
}
}
}
Java理论与实践: 平衡测试,第3部分:用方面检验设计约束(4)
时间:2010-12-22 IBM Brian Goetz
要让清单 5 中的 DebuggingLock 版本有帮助,程序必须在测试时实际地发 生死锁。因为死锁通常依赖于计时和环境,所以清单 5 中的方法可能还不够。清单 6 显示了另一个版本的 DebuggingLock,它不仅判断是否发生死锁,还会 判断给定的一对锁是否由多个线程在不一致的顺序下得到。每次得到锁时,它都 查看已经持有的锁的集合,对于每个锁,都记住在这个锁之前某个线程已经请求 了这些锁。在试图获得锁之前,lock() 方法都查看已经持有的锁,如果在这个 锁之后已经得到了其中一个锁,就抛出 AssertionError。这个实现的空间开销 要比前一个版本大得多(因为需要跟踪在给定锁之前所有已经得到的锁),但是 它能检测到更大泛围的 bug。它不会检测出所有可能的死锁 —— 只有由两个特 定锁之间的不一致顺序造成的死锁,而这是最常见的情况。
清单 6. DebuggingLock 的替代版本,即使死锁没有后果,也能检查出不一 致的锁定顺序
public class OrderHistoryLock extends ReentrantLock {
private static ThreadLocal<Set<OrderHistoryLock>> heldLocks =
new ThreadLocal<Set<OrderHistoryLock>>() {
public Set<OrderHistoryLock> initialValue() {
return new HashSet<OrderHistoryLock>();
}
};
private final Map<Lock, Boolean> predecessors
= new ConcurrentHashMap<Lock, Boolean>();
public OrderHistoryLock() { super(); }
public OrderHistoryLock(boolean fair) { super(fair); }
public void lock() {
boolean alreadyHeld = isHeldByCurrentThread();
for (OrderHistoryLock lock : heldLocks.get()) {
if (lock.predecessors.containsKey(this))
|