缓存学习(二):Guava Cache

本文详细介绍了Guava Cache的两种实现——LoadingCache和Cache,以及它们的创建和使用方法。通过CacheLoader配置LoadingCache的默认值逻辑,展示了CacheBuilder的各种可配置属性,如容量限制、过期策略、并发控制、监听器和统计信息。通过实例演示了如何通过CacheBuilder创建和定制缓存行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 LoadingCache的创建:借助CacheLoader

2 Cache的创建与使用:需要配置Callable

3 CacheBuilder的可配置属性


Guava是Google推出的Java增强包,包含了很多实用功能,如:Optional、数学工具、字符串工具等,像Optional特性已经被Java吸收,成为Java 8特性之一。Guava Cache也是Guava中包含的一个本地缓存工具,在内存中实现,提供了线程安全的实现机制。它有两个实现:LoadingCache和Cache,这两种实现都包含了默认值逻辑,即如果从缓存中取值时出现miss,则按照默认逻辑生成默认值存入缓存。

1 LoadingCache的创建:借助CacheLoader

 LoadingCache的默认值逻辑依靠CacheLoader实现,下面是一个创建LoadingCache的例子:

LoadingCache<Integer,String> loadingCache=CacheBuilder.newBuilder()
    .expireAfterWrite(5, TimeUnit.SECONDS)
    .initialCapacity(5)
    .maximumSize(10)
    .recordStats()
    .removalListener(removalNotification -> System.out.println(removalNotification.getCause()))
    .build(new CacheLoader<Integer, String>() {
        @Override
        public String load(Integer key) throws Exception {
            return "default-value";
        }
    });

这里配置了初始容量为5,最大容量为100,元素在写入缓存后5秒过期,miss时默认存入“default-value”,元素移除时输出移除原因,并且记录统计信息的缓存。下面对它进行测试:

loadingCache.put(1,"hello world");
for(int i=0;i<10;i++){
    String s=loadingCache.get(1);
    System.out.println(s);
    Thread.sleep(1000);
}
System.out.println("Statistics:");
System.out.println(loadingCache.stats().toString());

 这里先存入了一个对象,然后在循环中每隔一秒取一次值,期望是前五次取值为“hello world”,然后该元素因过期移除,并插入新元素“default-value”,且因此造成一次miss。执行后的输出如下:

hello world
hello world
hello world
hello world
hello world
EXPIRED
default-value
default-value
default-value
default-value
default-value
Statistics:
CacheStats{hitCount=9, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3397100, evictionCount=1}

符合期望。 

2 Cache的创建与使用:需要配置Callable

通过上面的例子可以看出,LoadingCache属于一次配置,永久生效,除非重新创建,否则无法更改默认值生成逻辑,虽然方便使用但是不够灵活。Cache可以在每次get时都设置Callable,使用它来生成默认值,会更加灵活。

Cache<Integer,String> cache= CacheBuilder.newBuilder()
    .maximumSize(100)
    .recordStats()
    .expireAfterWrite(2,TimeUnit.SECONDS)
    .removalListener(removalNotification -> System.out.println(removalNotification.getCause()))
    .build();
cache.put(1,"hello world");
for(int i=0;i<10;i++){
    final int tmp=i;
    String s=cache.get(1, () -> tmp+"default-value");
    System.out.println(s);
    Thread.sleep(1000);
}
System.out.println("Statistics:");
System.out.println(cache.stats().toString());

 这里生成的默认值是用循环批次加上“default-value”组成的,且设定缓存2秒后过期。运行后效果如下:

hello world
hello world
EXPIRED
2default-value
2default-value
EXPIRED
4default-value
4default-value
EXPIRED
6default-value
6default-value
EXPIRED
8default-value
8default-value
Statistics:
CacheStats{hitCount=6, missCount=4, loadSuccessCount=4, loadExceptionCount=0, totalLoadTime=2305855, evictionCount=4}

3 CacheBuilder的可配置属性

上述的两种缓存创建方式,都是通过CacheBuilder创建的,在创建过程中,我们调用CacheBuilder的方法对缓存进行了一系列属性配置,CacheBuilder支持的所有配置项如下:

1)缓存清除策略配置:

Guava Cache可配置的自动缓存清除策略有三种:

  • 基于容量清除:
    • maximumSize(int size):设置缓存最多可以存放多少个元素
    • expireAfterAccess(long duration,TimeUnit unit):设置元素在每次访问后多久过期
  • 基于过期时间清除:
    • expireAfterWrite(long duration,TimeUnit unit):设置元素在每次写入后多久过期
  • 基于引用清除:
    • softValues()/weakKeys()/weakValues():设置值/键为软引用或弱引用,以便JVM及时回收对象

其中基于容量的清除比较复杂,以Cache接口为例,其实现类LocalManualCache的put方法会在插入新数据后就立即尝试驱逐刚刚插入的Entry:

++this.modCount;
e = this.newEntry(key, hash, first);
this.setValue(e, key, value, now);
table.set(index, e);
newCount = this.count + 1;
this.count = newCount;
this.evictEntries(e);

evictEntries会判断两个条件:当前是否是按照容量自动清除,以及刚刚插入的数据的权值是否大于设置最大权重,是则删除该Entry,否则寻找下一个可以驱逐的元素进行清除。这里的权重有两种配置方式,如果通过maximumWeight配置了最大权重,并通过weigher配置了元素权重计算函数,则会按照配置进行计算和比较,否则就把最大容量当作最大权重,而默认的元素权值都是1。

2)并发控制:

concurrencyLevel(int concurrencyLevel):设置可以同时写缓存的线程数

3)监听器:

可以为Guava Cache配置一个移除事件监听器,以便获取哪个元素被移除,以及移除原因,通过removalListener函数配置,该监听器默认同步执行,如果监听器执行的逻辑是耗时操作,需要使用RemovalListeners.asynchronous()方法将同步监听器封装成异步监听器。

4)统计:

在上面的两个例子中,最后都打出了统计信息,该特性需要显式调用recordStats()方法,否则输出的数据全是0。统计信息包括:命中次数、命中率、未命中次数、未命中率、加载次数、加载成功次数、加载异常次数、加载异常率、总加载时间、平均单次加载用时、清除次数。

5)Ticker:

Ticker可以用来指定时间源,默认是系统提供的System.nanoTime()时间源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值