0

我知道 Guava Cache 允许为单个缓存配置到期时间。Guava 是否使用在配置的秒数后唤醒缓存以使缓存无效的计时器来执行此操作?

我有一个长期运行的事务。无论事务开始时缓存中的内容是什么,我都希望它一直持续到事务结束。因此,即使缓存的有效秒数在事务期间过期,从缓存访问的值应该保持不变,直到我们到达事务结束。这在番石榴中可行吗?

谢谢,
亚什

4

1 回答 1

3

什么时候开始清理?· CachesExplained · google/guava Wiki

构建的缓存不会CacheBuilder“自动”或在值过期后立即执行清理和逐出值,或任何类似的操作。相反,它在写入操作期间执行少量维护,或者如果写入很少,则在偶尔的读取操作期间执行。

这样做的原因是:如果我们想Cache 持续进行维护,我们需要创建一个线程,它的操作会与用户操作竞争共享锁。此外,某些环境会限制线程的创建,这会导致CacheBuilder在该环境中无法使用。

相反,我们将选择权交给您。如果您的缓存是高吞吐量的,那么您不必担心执行缓存维护以清理过期条目等。如果您的缓存很少写入并且您不希望清理以阻止缓存读取,您可能希望创建自己的维护线程 Cache.cleanUp()以定期调用。

如果您想为很少有写入的缓存安排定期缓存维护,只需使用 ScheduledExecutorService.

因此,如果你只是在做阅读,那么如果你Cache.cleanUp()在交易之前和之后做一个,你“可能”会很好,但仍然不能保证。

但是,与其试图强制项目保留在缓存中,不如使用 a 将项目驱逐到另一个缓存/映射removalListener,然后当您阅读时,您首先需要检查缓存,然后,如果它不存在,请检查在长时间运行的事务中被驱逐的项目。

下面是一个过于简单的例子:

Map<Integer, String> evicted = new ConcurrentHashMap<>();
Cache<Integer, String> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(2, SECONDS)
        .removalListener((RemovalListener<Integer, String>) notification -> 
                evicted.put(notification.getKey(), notification.getValue()))
        .build();
assert evicted.size() == 0 && cache.size() == 0;
cache.put(0, "a");
cache.put(1, "b");
cache.put(2, "c");
assert evicted.size() == 0 && cache.size() == 3;
sleepUninterruptibly(1, SECONDS);
assert evicted.size() == 0 && cache.size() == 3;
cache.put(3, "d");
assert evicted.size() == 0 && cache.size() == 4;
sleepUninterruptibly(1, SECONDS);
cache.cleanUp();
assert evicted.size() == 3 && cache.size() == 1;
Integer key = 2;
String value;
{
    value = cache.getIfPresent(key);
    if (value == null) value = evicted.get(key);
}
assert Objects.equals(value, "c");

如果您同时运行长时间运行的事务或在具有不同驱逐策略的线程之间使用公共缓存等,您的实际代码将需要有条件put地进入evicted、清理evicted、管理多个evicted对象,但希望这足以证明这个想法让您开始了。

于 2016-08-31T14:44:16.353 回答