线程安全更新缓存资源

时间:2013-08-23 20:39:42

标签: java multithreading caching guava

逻辑很简单:

Foo foo = cache.get();
if (isUpToDate(foo)) {
    return foo;
} else {
    foo = getUpdatedFoo(); // slow or expensive
    cache.put(foo);
    return foo;
}

但是,我想确保

  1. 一次只有一个线程调用getUpdatedFoo()
  2. 如果线程A已经调用getUpdatedFoo(),则线程B不会 叫它,而不是等待线程A的结果
  3. 我可能会根据JCiP的Memoizer模式拼凑一些东西,但我怀疑有一种更简单的方法 - 可能使用Guava CacheBuilder?但是,如何不是很明显。


    更新:根据以下FrankPL's answer实施双重检查锁定模式:

    Foo foo = cache.get();
    if (!isUpToDate(foo)) {
        lock.lock(); // Will block if some other thread is refreshing
        try {
            // See if some other thread already refreshed for us
            foo = cache.get();
            if (!isUpToDate(foo)) {
                // guess not, we'll refresh it ourselves
                foo = getUpdatedFoo();
                cache.put(foo);
            }
        } finally {
            lock.unlock();
        }
    }
    return foo;
    

2 个答案:

答案 0 :(得分:0)

有关您可以使用的双重检查锁定模式,请参阅http://en.wikipedia.org/wiki/Double-checked_locking。请使用您的new Helper()

,而不是代码示例中使用的getUpdatedFoo()

答案 1 :(得分:0)

也许您可以考虑使用读写锁(http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html)。下面给出了一个示例代码:

rwlock.readLock().lock();
Foo foo = cache.get();
if(isUpToDate(foo)) {
    rwlock.readLock.unlock();
    return foo;
} else {
    rwlock.readLock.unlock();
    rwlock.writeLock.lock();
    // recheck if updated
    Foo foo = cache.get();
    if(isUpToDate(foo)) {
        rwlock.writeLock.unlock();
        return foo;
    } else {
        foo = getUpdatedFoo();
        cache.put(foo);
        rwlock.writeLock.unlock();
        return foo;
    }
}