Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么不';t缓存元素是否会立即过期?_Java_Caching_Guava - Fatal编程技术网

Java 为什么不';t缓存元素是否会立即过期?

Java 为什么不';t缓存元素是否会立即过期?,java,caching,guava,Java,Caching,Guava,我有一个我认为是一个简单的使用番石榴缓存。然而,这种行为对我来说并不直观。我有一个POJO,Foo,属性为Id(Integer)。在检索Foo的实例时,我使用Integer作为缓存的键。如果我在缓存中放入三个项目,并睡眠足够长的时间,使所有内容都过期,那么无论键值是多少,我都会期待相同的行为。问题是,我看到了基于所用密钥的不同行为。我将三个对象放入缓存:1000、2000和3000 [main]信息缓存测试用例-3000创建foo,1000 [main]信息缓存测试用例-3000创建foo,20

我有一个我认为是一个简单的使用番石榴缓存。然而,这种行为对我来说并不直观。我有一个POJO,
Foo
,属性为
Id
Integer
)。在检索
Foo
的实例时,我使用
Integer
作为缓存的键。如果我在缓存中放入三个项目,并睡眠足够长的时间,使所有内容都过期,那么无论键值是多少,我都会期待相同的行为。问题是,我看到了基于所用密钥的不同行为。我将三个对象放入缓存:1000、2000和3000

[main]信息缓存测试用例-3000创建foo,1000
[main]信息缓存测试用例-3000创建foo,2000
[main]信息缓存测试用例-3000创建foo,3000
[main]INFO CacheTestCase-3000休眠以让某些缓存过期。
[main]信息缓存测试用例-3000继续。
[main]信息缓存测试用例-3000已删除,1000
[main]信息缓存测试用例-3000已删除,2000年
[main]信息缓存测试用例-3000创建foo,1000
[main]信息缓存测试用例-
注意,在上面的运行中,键为3000的Foo实例没有从缓存中删除。下面是相同代码的输出,但我使用了4000而不是3000键

[main]信息缓存测试用例-4000创建foo,1000
[main]信息缓存测试用例-4000创建foo,2000
[main]信息缓存测试用例-4000创建foo,4000
[main]INFO CacheTestCase-4000休眠以让某些缓存过期。
[main]信息缓存测试用例-4000继续。
[main]信息缓存测试用例-删除4000,删除1000
[main]信息缓存测试用例-4000已删除,2000年
[main]信息缓存测试用例-4000已删除,4000
[main]信息缓存测试用例-4000创建foo,1000
当然,我做了一些难以置信的蠢事。这是我的MCVE:

package org.dlm.guava;

import com.google.common.cache.*;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Created by dmcreynolds on 8/17/2015.
 */
public class CacheTestCase {
    static final Logger log = LoggerFactory.getLogger("CacheTestCase");
    String p = ""; // just to make the log messages different
    int DELAY = 10000; // ms
    @Test
    public void testCache123() throws Exception {
        p = "3000";
        LoadingCache<Integer, Foo> fooCache = CacheBuilder.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(100, TimeUnit.MILLISECONDS)
                .removalListener(new FooRemovalListener())
                .build(
                        new CacheLoader<Integer, Foo>() {
                            public Foo load(Integer key) throws Exception {
                                return createExpensiveFoo(key);
                            }
                        });

        fooCache.get(1000);
        fooCache.get(2000);
        fooCache.get(3000);
        log.info(p + " Sleeping to let some cache expire . . .");
        Thread.sleep(DELAY);
        log.info(p + " Continuing . . .");
        fooCache.get(1000);
    }


    private Foo createExpensiveFoo(Integer key) {
        log.info(p+" creating foo, " + key);
        return new Foo(key);
    }


    public class FooRemovalListener
        implements RemovalListener<Integer, Foo> {
        public void onRemoval(RemovalNotification<Integer, Foo> removal) {
            removal.getCause();
            log.info(p+" Removed, " + removal.getKey().hashCode());
        }
    }

    /**
     * POJO Foo
     */
    public class Foo {
        private Integer id;

        public Foo(Integer newVal) {
            this.id = newVal;
        }

        public Integer getId() {
            return id;
        }
        public void setId(Integer newVal) {
            this.id = newVal;
        }
    }
}
package org.dlm.guava;
导入com.google.common.cache.*;
导入org.junit.Test;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入java.util.concurrent.TimeUnit;
/**
*由dmcreynolds于2015年8月17日创建。
*/
公共类CacheTestCase{
静态最终记录器日志=LoggerFactory.getLogger(“CacheTestCase”);
字符串p=”“;//只是为了使日志消息不同
int DELAY=10000;//毫秒
@试验
public void testCache123()引发异常{
p=“3000”;
LoadingCache fooCache=CacheBuilder.newBuilder()
.最大尺寸(1000)
.expireAfterWrite(100,时间单位为毫秒)
.removalListener(新的FooRemovalListener())
.建造(
新缓存加载程序(){
公共Foo加载(整数键)引发异常{
返回createExpensiveFoo(键);
}
});
fooCache.get(1000);
fooCache.get(2000);
fooCache.get(3000);
log.info(p+“睡眠以让某些缓存过期…”);
睡眠(延迟);
日志信息(p+“继续…”);
fooCache.get(1000);
}
私有Foo createExpensiveFoo(整数密钥){
log.info(p+“创建foo,”+键);
返回新的Foo(key);
}
公共类FooRemovalListener
实现RemovalListener{
移除时的公共无效(移除通知移除){
删除.getCause();
log.info(p+“已删除,”+removation.getKey().hashCode());
}
}
/**
*博若福
*/
公开课Foo{
私有整数id;
公共Foo(整数newVal){
this.id=newVal;
}
公共整数getId(){
返回id;
}
公共void setId(整数newVal){
this.id=newVal;
}
}
}
来自Javadoc,用于:

如果请求了
expireAfterWrite
expireAfterAccess
,则在每次修改缓存、偶尔访问缓存或调用
cache.cleanUp()
时,可能会逐出条目。过期的条目可以通过缓存.size()计数,但读或写操作永远看不到

有一件事是,一旦过期,如果你试图阅读任何过期的条目,你会发现它们不再存在。例如,尽管在
RemovalListener
中没有看到
3000
的条目被删除,但如果调用
fooCache.get(3000)
,它必须首先加载该值(此时会看到旧值被删除)。因此,从缓存API用户的角度来看,旧的缓存值消失了

您在示例中看到特定行为的原因很简单:缓存因并发原因被分割。条目根据其散列码分配一个段,每个段的作用类似于一个独立的小型缓存。因此,大多数操作(如
fooCache.get(1000)
)将只在单个段上操作。在您的示例中,
1000
2000
被明确分配到同一段,而
3000
位于另一段<在您的第二个版本中,code>4000被分配到与
1000
2000
相同的段,因此当写入
1000
的新值时,它将与其他两个段一起被清除

在大多数实际使用中,段通常应该经常被点击,以便定期清理过期条目,而不会成为问题。但是,除非您在缓存上调用
cleanUp()
,否则无法保证这将在何时发生。

维护不会在超时发生后立即发生。 从(我的)重点:

什么时候清理? 使用
CacheBuilder
构建的缓存不会“自动”执行清除和逐出值,也不会在值过期后立即执行清除和逐出值,或者执行任何类似操作相反,它执行sma