【发布时间】:2019-04-09 03:39:48
【问题描述】:
我使用 WeakHashMap 和 ReentrantReadWriteLock 实现了一个缓存, 我的代码是这样的:
class Demo<T, K> {
private final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
private final Map<T, K> CACHE = new WeakHashMap<>();
public K get(T t) {
ReentrantReadWriteLock.ReadLock readLock = LOCK.readLock();
ReentrantReadWriteLock.WriteLock writeLock = LOCK.writeLock();
readLock.lock();
if(CACHE.containsKey(t)){
//-- question point --
K result = CACHE.get(t);
readLock.unlock();
return result;
}
readLock.unlock();
K result = // find from db;
writeLock.lock();
CACHE.put(t,result);
writeLock.unlock();
return result;
}
}
我的问题是,gc 在if(CACHE.containsKey(t)) 之后但在K result = CACHE.get(t); 之前执行读锁并导致if(CACHE.containsKey(t)) 为真但K result = CACHE.get(t); 为空。
【问题讨论】:
-
作为一个侧节点,你使用锁定的方式将来会给你带来问题(例如,解锁两次,没有安全措施)。在 lock 类的 javadoc 中是一个如何安全使用锁的示例(通过使用 try-finally 块)。
-
只是不要使用这种双重查找。只需使用
K result = CACHE.get(t); if(result != null) return result;。问题解决了。除此之外,您正在释放读锁,然后获取写锁;如果另一个线程在这两个步骤之间设置了一个值,您将覆盖映射。一旦获得写锁,您必须重新检查。考虑putIfAbsent(如果您使用的是 Java 8)。 -
@Holger 非常感谢您的建议,这对我很有用。
标签: java garbage-collection jvm reentrantreadwritelock