ehcache和多线程:如何在插入缓存时锁定?

时间:2018-11-02 19:54:47

标签: ehcache

假设我有一个多线程应用程序,其中有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
}

2 个答案:

答案 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
}