7

如果未写入密钥,我已经创建了一个基于 Guava CacheBuilder 的缓存,其有效期为 5 秒。添加了一个removingListener 来打印被移除的键/值对。我观察到的是侦听器的 onRemoval 方法仅在第一次被调用。第二次删除条目时不会调用它。(实际的删除发生了。只是没有调用removingListener 的onRemoval 方法)。

难道我做错了什么?有人可以帮忙吗?提前致谢。这是我的代码:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;


public class TestCacheBuilder {

  public static void main(String[] args) {
    try {
      new TestCacheBuilder();
    }catch (Exception e){      
     e.printStackTrace(); 
    }
  }

  public TestCacheBuilder() {

    Cache<String, String> myCache = CacheBuilder.newBuilder()
        .expireAfterWrite(5, TimeUnit.SECONDS)
        .removalListener(new RemovalListener<String, String>() {
          public void onRemoval(RemovalNotification<String, String> removal) {
            System.out.println("removal: "+removal.getKey()+"/"+removal.getValue());
          }          
        })
        .build();


    Map<String, String> inMap = myCache.asMap();

    inMap.put("MyKey", "FirstValue");

    System.out.println("Initial Insert: "+inMap);

    //Wait 16 seconds

    try {
      Thread.sleep(4000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }

    System.out.println("After 4 seconds: " + inMap);

    inMap.put("MyKey", "SecondValue");

    try {
      Thread.sleep(1000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }

    System.out.println("After 1 more second: " + inMap);

    try {
      Thread.sleep(4000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }


    System.out.println("After 4 more seconds: " + inMap);

  }

}

输出如下:

Initial Insert: {MyKey=FirstValue}
After 4 seconds: {MyKey=FirstValue}
removal: MyKey/FirstValue
After 1 more second: {MyKey=SecondValue}
After 4 more seconds: {}
4

3 回答 3

12

删除实际上并不直接发生:Guava 没有自己的清理线程来删除过期条目。删除通常发生在缓存的同一段中有写入时,或者在经过一定时间后的读取期间(以摊销删除成本)。但是,虽然条目仍然存在,但它被视为已过期,这就是它没有被打印的原因。

引用CacheBuilder 的 javadoc

如果 expireAfterWrite 或 expireAfterAccess 被请求,条目可能会在每次缓存修改、偶尔缓存访问或调用 Cache.cleanUp() 时被驱逐。过期的条目可能会被计入 Cache.size(),但永远不会对读取或写入操作可见。

于 2012-08-28T10:48:07.213 回答
1

作为 Guava 的替代品,Cache您可以使用Caffeine“基于 Java 8 的高性能、近乎最佳的缓存库”

它具有“受 Google Guava 启发的 API”并定期执行驱逐,因此它可能更适合您的用例(如果您担心内存不会被半自动释放)。请参阅驱逐维基页面

在写入期间和偶尔在读取期间通过定期维护执行过期。到期事件的调度和触发在分摊的 O(1) 时间内执行。

于 2018-03-11T08:21:14.563 回答
0

如果expireAfterWrite或被expireAfterAccess请求,条目可能会在每次缓存修改、偶尔缓存访问或调用Cache.cleanUp(). 过期的条目可能会被计入Cache.size(),但永远不会对读取或写入操作可见。

从长远来看,这最终会吃掉所有堆内存或导致其他未使用任何缓存的模块的内存不足。

于 2014-08-20T03:27:03.360 回答