Google Guava Cache

JAVA 8

guava 31.1-jre

---

 

序章

Guava is a suite of core and expanded libraries that include utility classes, Google's collections, I/O classes, and much more.

Guava Cache 的优点是封装了get,put操作;提供线程安全的缓存操作;提供过期策略;提供回收策略;缓存监控。当缓存的数据超过最大值时,使用LRU算法替换。

maven项目引入Guava:ben发布于博客园

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>31.1-jre</version>
</dependency>

注,最新 Mar 01, 2022,31.1-jre,一年多没更新了。

 

一个本地缓存

 

最简单的使用

初始化一个 LoadingCache 对象(本地缓存),测试 读写数据。ben发布于博客园

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;

/**
 * Guava Cache 测试
 */
@Slf4j
public class GuavaCacheTest {

    public static LoadingCache<String, Integer> cb;

    static {
        cb = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        return null;
                    }
                });
    }

    public static void main(String[] args) {
        checkCb(cb);

        test1();
    }

    private static void test1() {
        String key = "key1";
        Integer val = null;
        try {
            val = cb.get(key);
            log.info("val 1={}", val);
        } catch (Exception e) {
            log.error("异常 1:e=", e);
        }

        cb.put(key, 1);

        try {
            val = cb.get(key);
            log.info("val 2={}", val);
        } catch (Exception e) {
            log.error("异常 2:e=", e);
        }

        checkCb(cb);

    }

    private static void checkCb(LoadingCache<String, Integer> cb) {
        log.info("cb={}, stats={}", cb, cb.stats());
    }
}

测试结果:

cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0, 
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
异常 1:e=
com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key key1.
	at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2319)
	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2283)
	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2159)
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2049)
	at com.google.common.cache.LocalCache.get(LocalCache.java:3966)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3989)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4950)
	at com.lib.bootweb.test.GuavaCacheTest.test1(GuavaCacheTest.java:38)
	at com.lib.bootweb.test.GuavaCacheTest.main(GuavaCacheTest.java:31)
val 2=1
cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0, 
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}

key 对应的值不存在时,返回异常。存在时,返回值。ben发布于博客园

由于 get(key) 会返回异常,可以使用 getIfPresent(key) 获取——不存在时返回null。

 

getIfPresent测试

    private static void test2() {
        IntStream.range(0, 5).forEach(cnt->{
            String key = "nokey" + cnt;
            Integer val = cb.getIfPresent(key);
            System.out.println(key + ", val=" + val);
        });
    }

测试结果:

cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0, 
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
nokey0, val=null
nokey1, val=null
nokey2, val=null
nokey3, val=null
nokey4, val=null

通过其返回值,可以判断 缓存中是否存在该值。ben发布于博客园

 

更多基本使用

asMap、invalidate

    private static void test3() {
        IntStream.range(0, 5).forEach(i->{
            cb.put("key" + i, i * i);
        });

        // asMap()
        log.info("1.cb.size={}", cb.size());
        ConcurrentMap<String, Integer> cbMap = cb.asMap();
        log.info("Map={}, class={}, values={}", cbMap.size(), cbMap.getClass(), cbMap);

        // 使用 map 直接添加
        cbMap.put("mapAdd", 123);
        log.info("2.cb.size={}", cb.size());
        log.info("Map={}, {}", cb.asMap(), cb.asMap().size());

        // invalidate 删除元素
        cb.invalidate("mapAdd");
        cb.invalidate("key0");
        cb.invalidate("key1");
        log.info("3.cb.size={}", cb.size());
        log.info("Map={}, {}", cb.asMap(), cb.asMap().size());
    }

测试结果:

cb=com.google.common.cache.LocalCache$LocalLoadingCache@3b22cdd0, stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, 
loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
1.cb.size=5
Map=5, class=class com.google.common.cache.LocalCache, values={key4=16, key1=1, key2=4, key0=0, key3=9}
2.cb.size=6
Map={mapAdd=123, key4=16, key1=1, key2=4, key0=0, key3=9}, 6
3.cb.size=3
Map={key4=16, key2=4, key3=9}, 3

asMap() 返回了一个  ConcurrentMap<String, Integer> 对象,具体类型为 com.google.common.cache.LocalCache。

 

添加、获取、获取所有、删除,初步试验完毕。ben发布于博客园

至于 批量添加、批量删除、清空 等,大家可以自行摸索。

 

看源码

LoadingCache 接口

package com.google.common.cache;

@GwtCompatible
@ElementTypesAreNonnullByDefault
public interface LoadingCache<K, V> extends Cache<K, V>, Function<K, V> {
}

实现类:ben发布于博客园

 

Cache接口

package com.google.common.cache;

@DoNotMock("Use CacheBuilder.newBuilder().build()")
@GwtCompatible
@ElementTypesAreNonnullByDefault
public interface Cache<K, V> {
}

 

Function接口

package com.google.common.base;

@GwtCompatible
@FunctionalInterface
@ElementTypesAreNonnullByDefault
public interface Function<F extends @Nullable Object, T extends @Nullable Object>
    extends java.util.function.Function<F, T> {
}

 

 

CacheBuilder 类

@GwtCompatible(emulated = true)
@ElementTypesAreNonnullByDefault
public final class CacheBuilder<K, V> {
}

 

进一步配置

上面的 LoadingCache<String, Integer> 基本上没有配置,这样的话,可以往这个 cb 缓存对象中 存取无限量的数据——最终把程序搞挂。

最佳实践是,根据项目需要、业务需要做适当的配置,避免误操作。ben发布于博客园

这些配置函数在 CacheBuilder 类 中,上面已展示,参考文档2中也有更多示例。

 

下面配置 缓存容量最大值 和 写入后10秒过期:

static {
        cb = CacheBuilder.newBuilder()
                // 最大容量 10
                .maximumSize(10)
                // 写入后 10秒 过期
                .expireAfterWrite(Duration.ofSeconds(10))
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        return null;
                    }
                });
    }

测试程序:ben发布于博客园

    private static void test4() {
        log.info("1.put");
        IntStream.range(0, 15).forEach(i->{
            cb.put("key_" + i, i * i);
        });
        log.info("cb={}, {}", cb.size(), cb.asMap());

        log.info("2.get");
        IntStream.range(0, 15).forEach(i->{
            log.info("i={}, val={}", i, cb.getIfPresent("key_" + i));
        });

        try {
            TimeUnit.SECONDS.sleep(12);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.info("3.get");
        IntStream.range(0, 15).forEach(i->{
            log.info("i={}, val={}", i, cb.getIfPresent("key_" + i));
        });

        log.info("cb={}, {}", cb.size(), cb.asMap().size(), cb.asMap());
    }

测试结果:ben发布于博客园

测试结果
 21:33:43.186 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=com.google.common.cache.LocalCache$LocalLoadingCache@2c13da15, 
stats=CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
21:33:43.206 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 1.put
21:33:43.258 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=10, {key_12=144, key_14=196, key_5=25, key_6=36, key_8=64, key_9=81, 
key_11=121, key_7=49, key_13=169, key_10=100}
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 2.get
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=0, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=1, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=2, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=3, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=4, val=null
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=5, val=25
21:33:43.260 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=6, val=36
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=7, val=49
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=8, val=64
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=9, val=81
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=10, val=100
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=11, val=121
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=12, val=144
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=13, val=169
21:33:43.261 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=14, val=196
21:33:55.269 [main] INFO com.lib.bootweb.test.GuavaCacheTest - 3.get
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=0, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=1, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=2, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=3, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=4, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=5, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=6, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=7, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=8, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=9, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=10, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=11, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=12, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=13, val=null
21:33:55.270 [main] INFO com.lib.bootweb.test.GuavaCacheTest - i=14, val=null
21:33:55.274 [main] INFO com.lib.bootweb.test.GuavaCacheTest - cb=0, 0

“2.get”后面,只有 10条数据,和 “maximumSize(10)”有关;ben发布于博客园

休眠 12秒后,缓存中没有数据,和 “expireAfterWrite(Duration.ofSeconds(10))”有关。

注意区分 expireAfterWrite 和 expireAfterAccess。

 

---END---

ben发布于博客园

本文链接:

https://www.cnblogs.com/luo630/p/17212894.html

ben发布于博客园

参考资料

1、Guava LoadingCache详解及工具类

https://www.codenong.com/cs105556768/

2、Guava学习笔记:Guava cache

https://www.cnblogs.com/peida/p/Guava_Cache.html

3、Guava Cache 原理分析与最佳实践
by Java Punk
于 2022-07-27 16:41:39 发布
原文链接:https://blog.csdn.net/weixin_44259720/article/details/125890496

4、

 

ben发布于博客园

 

posted @ 2023-03-13 21:38  快乐的凡人721  阅读(85)  评论(0编辑  收藏  举报