假设我有一个多线程应用程序,其中有4个线程共享一个(Eh)缓存;缓存会存储UserProfile对象,以避免每次从数据库中获取它们。
现在,假设所有这四个线程同时请求ID为123的同一UserProfile-并且尚未被缓存。必须要做的是查询数据库并将获得的UserProfile对象插入缓存,以便以后可以重用。
但是,我要实现的是这些线程中只有一个(第一个)查询数据库并更新缓存,而其他3个(队列)等待它完成...然后获取UserProfile ID = 123的对象直接从缓存中获取。
您通常如何实现这种情况?是使用Ehcache的锁定/交易吗?还是通过这样的事情? (伪代码)
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
}
答案 0 :(得分:0)
使用普通的Java对象锁:
private static final Object LOCK = new Object();
synchronized (LOCK) {
result = ehcache.get(id);
if ( result == null || ehcache.isExpired() ) {
// cache is expired or null so going to DB
result = database.fetchUserProfile(id);
ehcache.put(id, result)
}
}
答案 1 :(得分:0)
这称为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
}