【问题标题】:Guava CacheBuilder removal listenerGuava CacheBuilder 移除监听器
【发布时间】:2012-05-16 21:06:25
【问题描述】:

请告诉我我遗漏了什么。

我在 DataPool 中通过 CacheBuilder 构建了一个缓存。 DataPool 是一个单例对象,其实例各种线程都可以获取并对其进行操作。现在我有一个线程来生成数据并将其添加到上述缓存中。

显示代码的相关部分:

 private InputDataPool(){

    cache=CacheBuilder.newBuilder().expireAfterWrite(1000, TimeUnit.NANOSECONDS).removalListener(
            new RemovalListener(){
                {
                    logger.debug("Removal Listener created");
                }
                                public void onRemoval(RemovalNotification notification) {
                                    System.out.println("Going to remove data from InputDataPool");
                                    logger.info("Following data is being removed:"+notification.getKey());
                                    if(notification.getCause()==RemovalCause.EXPIRED)
                                    {
                                        logger.fatal("This data expired:"+notification.getKey());
                                    }else
                                    {
                                        logger.fatal("This data didn't expired but evacuated intentionally"+notification.getKey());
                                    }

                                }}
                    ).build(new CacheLoader(){

                        @Override
                        public Object load(Object key) throws Exception {
                                logger.info("Following data being loaded"+(Integer)key);
                                Integer uniqueId=(Integer)key;
                                return InputDataPool.getInstance().getAndRemoveDataFromPool(uniqueId);

                        }

                    });
}

public static InputDataPool getInstance(){
        if(clsInputDataPool==null){
            synchronized(InputDataPool.class){
                if(clsInputDataPool==null)
                {
                    clsInputDataPool=new InputDataPool();
                }
            }
        }
    return clsInputDataPool;
}

从上述线程进行调用就像

一样简单
 while(true){
 inputDataPool.insertDataIntoPool(inputDataPacket);
     //call some logic which comes with inputDataPacket and sleep for 2 seconds.
}

inputDataPool.insertDataIntoPool 是这样的

inputDataPool.insertDataIntoPool(InputDataPacket inputDataPacket){ 
 cache.get(inputDataPacket.getId());
}

现在的问题是,缓存中的元素应该在 1000 纳秒后过期。所以当第二次调用 inputDataPool.insertDataIntoPool 时,第一次插入的数据将被撤出,因为它必须在调用时过期插入后 2 秒后,应调用相应的移除侦听器。 但这并没有发生。我查看了缓存统计信息,无论调用多少时间 cache.get(id) ,evictionCount 始终为零。

但重要的是,如果我扩展 inputDataPool.insertDataIntoPool

  inputDataPool.insertDataIntoPool(InputDataPacket inputDataPacket){ 
 cache.get(inputDataPacket.getId());
    try{
     Thread.sleep(2000);
   }catch(InterruptedException ex){ex.printStackTrace();
     }
cache.get(inputDataPacket.getId())
}

然后驱逐按预期发生,并调用删除侦听器。

现在我非常无能为力,因为我错过了一些可以期待这种行为的东西。请帮我看看,如果你看到了什么。

附:请忽略任何拼写错误。也没有进行检查,没有使用泛型,因为这只是在测试 CacheBuilder 功能的阶段。

谢谢

【问题讨论】:

    标签: java caching guava


    【解决方案1】:

    正如 javadoc 和 user guide 中所解释的,没有线程可以确保在延迟过后立即从缓存中删除条目。相反,在写入操作期间会删除条目,如果写入很少,偶尔会在读取操作期间删除。这是为了允许高吞吐量和低延迟。当然,每个写操作都不会导致清理:

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

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

    【讨论】:

    • 同意!但在我所拥有的实施中,不是必须有一个一致的政策。正如我所提到的,当我在某些睡眠后重新触发 cache.get(key) 时,它会被驱逐,但是如果我想出一个不同的键并在相同的时间间隔后再次触发 cache.get(key),它不是驱逐。虽然我有代码,我会通过它,同时你能澄清一下吗,他们会使用一些关于驱逐的随机方法吗?
    • 尝试使用 cleanUp() 代替 get()
    • 这不是“随机的”,而是“尽力而为”的。此外,在具有高缓存吞吐量的实际应用程序中,您不必担心 - 缓存维护会非常频繁地发生。
    • 如果您需要具有超时功能的稳健时间限制调用,请使用 Guava TimeLimiter 接口和 SimpleTimeLimiter。然后将 expireAfterWrite 设置为允许的绝对最大超时值。
    【解决方案2】:

    我遇到了同样的问题,我可以在 guava 的 CacheBuilder.removalListener 文档中找到它

    警告:调用此方法后,请勿继续使用此缓存 建设者参考;而是使用此方法返回的引用。在 运行时,这些指向同一个实例,但只有返回的 引用具有正确的泛型类型信息,以确保 类型安全。为获得最佳结果,请使用标准的方法链接习惯用法 在上面的类文档中说明,配置构建器 并在单个语句中构建缓存。不注意这一点 通知可能会导致缓存抛出 ClassCastException 在未来某个未定义的时间点进行操作。

    因此,通过更改代码以使用添加删除列表后调用的构建器引用,可以解决此问题

     CacheBuilder builder=CacheBuilder.newBuilder().expireAfterWrite(1000, TimeUnit.NANOSECONDS).removalListener(
                new RemovalListener(){
                    {
                        logger.debug("Removal Listener created");
                    }
                                    public void onRemoval(RemovalNotification notification) {
                                        System.out.println("Going to remove data from InputDataPool");
                                        logger.info("Following data is being removed:"+notification.getKey());
                                        if(notification.getCause()==RemovalCause.EXPIRED)
                                        {
                                            logger.fatal("This data expired:"+notification.getKey());
                                        }else
                                        {
                                            logger.fatal("This data didn't expired but evacuated intentionally"+notification.getKey());
                                        }
    
                                    }}
                        );
       cache=builder.build(new CacheLoader(){
    
                            @Override
                            public Object load(Object key) throws Exception {
                                    logger.info("Following data being loaded"+(Integer)key);
                                    Integer uniqueId=(Integer)key;
                                    return InputDataPool.getInstance().getAndRemoveDataFromPool(uniqueId);
    
                            }
    
                        });
    

    这个问题将得到解决。它有点连线,但我想它就是这样:)

    【讨论】:

    • 这不是文档的意思。这意味着不要做CaceBuilder builder = CacheBuilder.newBuilder()..... 然后builder.removalListener(....)。它说改为做CaceBuilder builder = CacheBuilder.newBuilder().removalListener(...).....
    猜你喜欢
    • 2014-03-26
    • 1970-01-01
    • 2018-06-20
    • 2014-03-26
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 2014-11-29
    • 2011-12-30
    相关资源
    最近更新 更多