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))
throw new AssertionError("Possible deadlock between "
+ this + " and " + lock);
else if (!alreadyHeld)
predecessors.put(lock, Boolean.TRUE);
}
super.lock();
heldLocks.get().add(this);
}
public void unlock() {
super.unlock();
if (!isHeldByCurrentThread())
heldLocks.get().remove(this);
}
}
|