假设我有一个多线程应用程序,其中有 4 个线程共享一个 (Eh) 缓存;缓存存储 UserProfile 对象以避免每次都从数据库中获取它们。
现在,假设所有这 4 个线程同时请求 ID=123 的同一个 UserProfile - 而且它还没有被缓存。要做的是查询数据库并将获得的 UserProfile 对象插入到缓存中,以便以后可以重用。
但是,我想要实现的是这些线程中只有一个(第一个)查询数据库并更新缓存,而其他 3 个线程等待(排队)完成...然后获取 UserProfile直接从缓存中获取 ID=123 的对象。
你通常如何实现这种场景?使用Ehcache的locking/transactions?或者更确切地说是通过这样的事情? (伪代码)
public UserProfile getUserProfile(int id) {
result = ehcache.get(id)
if (result == null) { // not cached yet
synchronized { // queue threads
result = ehcache.get(id)
if (result == null) { // is current thread the 1st one?
result = database.fetchUserProfile(id)
ehcache.put(id, result)
}
}
}
return result
}
最佳答案
这叫做 Thundering Herd问题。
锁定有效,但它非常有效,因为锁比您想要的更宽。您可以锁定单个 ID。
你可以做两件事。一种是使用 CacheLoaderWriter .它将加载丢失的条目并以正确的粒度执行锁定。这是最简单的解决方案,即使您必须实现加载程序编写器。
备选方案更为复杂。您需要某种行锁定算法。例如,您可以这样做:
private final ReentrantLock locks = new ReentrantLocks[1024];
{
for(int i = 0; i < locks.length; i)) {
locks[i] = new ReentrantLock();
}
}
public UserProfile getUserProfile(int id) {
result = ehcache.get(id)
if (result == null) { // not cached yet
ReentrantLock lock = locks[id % locks.length];
lock.lock();
try {
result = ehcache.get(id)
if (result == null) { // is current thread the 1st one?
result = database.fetchUserProfile(id)
ehcache.put(id, result)
}
} finally {
lock.unlock();
}
}
return result
}
关于Ehcache & 多线程 : how to lock when inserting to the cache?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53125061/