关于J2EE中死锁问题的研究
http://tech.ddvip.com 2006年11月21日 社区交流
本文详细介绍关于J2EE中死锁问题的研究
为了说明此种死锁情形,我们以一个简单的(不完善的)read-through cache为例。该cache是数据库表中备份的HashMap。如果出现缓存命中,它就从HashMap返回一个值。但在缓存缺失的情况下,它将从数据库读取值,将其添加到HashMap,然后返回该值,如清单2所示。
清单 2public class SimpleCache {
private Map cache = new HashMap();
public synchronized Object get(String key) {
if (cache.containsKey(key)) {
return cache.get(key);
} else {
Object value = queryForValue(key);
cache.put(key, value);
return value;
}
}
private Object queryForValue(String key) {
return executeQuery("select value from cache_table " +
"where key='" + key + "'");
}
public synchronized void clearCache() {
cache.clear();
}
// other methods omitted for brevity
}
这是一个简单的遍历cache。注意:get()方法是同步的,这是因为我们访问了非线程安全容器,并要求containsKey/put组合在缓存缺失时是原子性的。
该cache相当简单易懂:它约定,如果更改支持缓存的表中的数据,则应调用clearCache(),这样缓存就可以避免处理陈旧的数据。产生的缓存缺失将相应地重新进入缓存。
我们现在来考虑可以更改此数据并清除缓存的代码:
public void updateData(String key, String value) {
executeUpdate("update cache_table set value='" + value +
"' where key='" + key + "'");
SimpleCache.getInstance().clearCache();
} 上面的代码在简单的例子中能正常运行。但是,在使用基于锁的并发控制的数据库中,updateData中的查询将阻止queryForValue中的选择查询的执行,因为update语句将获取一个写锁,从而阻止选择查询获取同一数据行上的读锁。如果同步没有问题,一个线程可以尝试读取缓存中的给定值,并在另一个线程在数据库中更新该值时得到缓存缺失。如果数据库先执行update语句,它将阻塞select语句继续执行。但是,执行select语句的线程来自同步的get方法,所以它获取了SimpleCache上的锁。要返回updateData中的线程,它必须调用clearCache(),但不能获取锁(clearCache()是同步的)。
来源:bea 作者:Michael Nonemacher 责编:豆豆技术应用