Java 理论与实践: 平衡测试,第 3 部分:用方面检验设计约束
2010-01-11 00:00:00 来源:WEB开发网要让清单 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))
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);
}
}
结束语
这里描述的方面属于策略实施方面。有些策略是应用程序设计的一部分,例如 “这些方法应当只从类 X 中调用” 或 “什么东西都不要使用 System.out 或 System.err”。其他策略是 API 的接口合约的一部分,例如 Swing 的单线程规则或 EJB 不应当创建线程或调用 AWT 之类的需求。在所有情况下,都可以在开发和测试中使用方面找出是否违犯了这些策略。不论是否在生产中使用方面,它都是测试工具包中的一个优秀工具。
更多精彩
赞助商链接