3

请告诉我我在哪里遗漏了一些东西。

我在 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 秒。然后相应地调用 Removal Listener。但这并没有发生。我查看了缓存统计信息,evictionCount 始终为零,无论调用多少时间 cache.get(id) 。

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

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

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

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

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

谢谢

4

2 回答 2

10

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

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

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

于 2012-05-16T22:16:04.793 回答
1

我有同样的问题,我可以在番石榴的 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);

                        }

                    });

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

于 2016-01-08T16:00:16.480 回答