Guava Cache

作者: 右耳菌 | 来源:发表于2022-06-09 17:17 被阅读0次

    Google Guava Cache是一种非常优秀本地缓存解决方案,提供了基于容量,时间和引用的缓存回收方式。

    • 基于容量的方式内部实现采用LRU算法,
    • 基于引用回收很好的利用了Java 虚拟机的垃圾回收机制。

    Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。Guava Cache为了限制内存占用,通常都设定为自动回收元素。

    maven 引用

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

    适用场景

    • 愿意消耗一些内存空间来提升速度
    • 预料到某些键会被多次查询
    • 缓存中存放的数据总量不会超出内存总量

    Tips: guava cache是运行在JVM的本地缓存,并不能把数据存放到外部服务器上。如果有这样的要求,因该尝试MemcachedRedis这类分布式缓存。


    Guava Cache 加载

    • 加载方式1 - CacheLoader
      LoadingCache是附带CacheLoader构建而成的缓存实现。创建自己的CacheLoader通常只需要简单地实现V load(K key) throws Exception方法。
    package cn.lazyfennec.guava;
    
    import com.google.common.cache.*;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 加载方式1:CacheLoader
     * 1.设置缓存容量
     * 2.设置超时时间
     * 3.提供移除监听器
     * 4.提供缓存加载器
     * 5.构建缓存
     *
     * @Author: Neco
     * @Description: 加载方式1:CacheLoader
     * @Date: create in 2022/6/9 16:22
     */
    public class GuavaCacheDemo1 {
    
        public static void main(String[] args) {
            // CacheLoader 初始化
            CacheLoader<String, Object> cacheLoader = new CacheLoader<String, Object>() {
                @Override
                // load方法的作用是在通过get方法从LoadingCache获取不到值时去加载该值并放入缓存。
                public String load(String key) throws Exception {
                    // 模拟加载值的过程
                    Thread.sleep(1000);
                    // 假设某个key是非法访问的
                    if ("err".equals(key)) {
                        return null;
                    }
                    return key + "'s value";
                }
            };
    
            // 移出监听器配置
            RemovalListener<String, Object> removalListener = new RemovalListener<String, Object>() {
                // 移出时触发的事件
                public void onRemoval(RemovalNotification<String, Object> removal) {
                    System.out.println("[" + removal.getKey() + ":" + removal.getValue() + "] is evicted!");
                }
            };
    
            //
            LoadingCache<String, Object> caches = CacheBuilder.newBuilder()
                    // 设置容量大小
                    .maximumSize(5)
                    // 设置超时时间
                    .expireAfterWrite(10, TimeUnit.SECONDS)
                    .expireAfterAccess(10, TimeUnit.SECONDS)
                    // 移出监听器
                    .removalListener(removalListener)
                    // 加载器配置
                    .build(cacheLoader);
    
            // 由于缓存的容易只设置了5个,存入10个就会由guava基于容量回收掉5个
            for (int i = 0; i < 10; i++) {
                String key = "key" + i;
                String value = "value" + i;
                caches.put(key, value);
                System.out.println("[" + key + ":" + value + "] is put into cache!");
            }
    
            // 如果存在就获取
            System.out.println(caches.getIfPresent("key6"));
    
            try {
                caches.get("err");
            } catch (Exception e) {
    //            e.printStackTrace();
                System.out.println("不存在key,会报错");
            }
        }
    
    }
    
    • 运行结果
    [key0:value0] is put into cache!
    [key1:value1] is put into cache!
    [key2:value2] is put into cache!
    [key3:value3] is put into cache!
    [key4:value4] is put into cache!
    [key0:value0] is evicted!
    [key5:value5] is put into cache!
    [key1:value1] is evicted!
    [key6:value6] is put into cache!
    [key2:value2] is evicted!
    [key7:value7] is put into cache!
    [key3:value3] is evicted!
    [key8:value8] is put into cache!
    [key4:value4] is evicted!
    [key9:value9] is put into cache!
    value6
    不存在key,会报错
    
    
    • 加载方式2 - Callable
      所有类型的Guava Cache,不管有没有自动加载功能,都支持get(K,Callable<V>)方法。这个方法返回缓存中相应的值,或者用给定的Callable运算并把结果加入到缓存中。在整个加载方法完成前,缓存项相关的可观察状态都不会更改。这个方法简便地实现了模式"如果有缓存则返回;否则运算、缓存、然后返回"。
    package cn.lazyfennec.guava;
    
    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    
    /**
     * 加载方式2:Callable
     * 所有类型的Guava Cache,不管有没有自动加载功能,都支持get(K, Callable<V>)方法。
     * 这个方法返回缓存中相应的值,或者用给定的Callable运算并把结果加入到缓存中。
     * 在整个加载方法完成前,缓存项相关的可观察状态都不会更改。
     * 这个方法简便地实现了模式"如果有缓存则返回;否则运算、缓存、然后返回"。
     *
     * @Author: Neco
     * @Description: 加载方式2:Callable
     * @Date: create in 2022/6/9 17:04
     */
    public class GuavaCacheDemo2 {
        // 构建容量为3的缓存对象
        static Cache<String, String> caches = CacheBuilder.newBuilder()
                .maximumSize(3)
                .build();
    
        public static void main(String[] args) {
            caches.put("1234", "我是存在的");
    
            // 如果存在就获取,不存在返回null
            System.out.println(caches.getIfPresent("key6"));
    
            try {
                // 获取key为123的缓存数据,如果有就返回,没有就返回call方法的返回值
                System.out.println(caches.get("123", new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        return "运算、缓存、然后返回";
                    }
                }));
    
                // 获取key为1234的缓存数据,如果有就返回,没有就返回call方法的返回值。注意这里key是存在的
                System.out.println(caches.get("1234", new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        return "我是打酱油的";
                    }
                }));
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 运行结果
    null
    运算、缓存、然后返回
    我是存在的
    

    Guaca Cache 缓存回收

    • 回收方式1 - 基于容量回收
      maximumSize(long):当缓存中的元素数量超过指定值时。
                    // 设置缓存容量
                    .maximumSize(5)
    
    • 回收方式2 - 定时回收
      expireAfterAccess(long,TimeUnit)expireAfterAccess(long,TimeUnit) :缓存项在给定时间内没有被读/写访问,则回收。
                    // 设置超时时间
                    .expireAfterWrite(10, TimeUnit.MINUTES)
                    .expireAfterAccess(10, TimeUnit.MINUTES)
    
    • 回收方式3 - 基于引用回收 (Reference-based Eviction)
      CacheBuilder.weakKeys():使用弱引用存储键。当key没有其它引用时,缓存项可以被垃圾回收。CacheBuilder.weakValues():使用弱引用存储值。当value没有其它引用时,缓存项可以被垃圾回收。CacheBuilder.softValues():使用软引用存储值,按照全局最近最少使用的顺序回收。
                // 当值没有其它(强或软)引用时,缓存项可以被垃圾回收
                .weakValues()
    

    Guaca Cache 显式清除

    任何时候都可以显式地清除缓存项,而不是等待被回收:

    • 个别清除: Cache.invalidate(key)
    • 批量清除:Cache.invalidateAll(keys)
    • 清除所有缓存项:Cache.invalidateAll()
    package cn.lazyfennec.guava;
    
    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    
    /**
     * @Author: Neco
     * @Description:
     * @Date: create in 2022/6/9 17:14
     */
    public class GuavaCacheDemo3 {
        static Cache<String, Object> testCache = CacheBuilder.newBuilder()
                // 当值没有其它(强或软)引用时,缓存项可以被垃圾回收
                .weakValues()
                // 开启Guava Cache的统计功能
                .recordStats()
                .build();
    
        public static void main(String[] args) {
            Object obj1 = new Object();
    
            testCache.put("1234", obj1);
    
            obj1 = new String("123");
    
            // 主动gc
            System.gc();
    
            System.out.println(testCache.getIfPresent("1234"));
    
            /*
            stats()方法会返回CacheS tats 对象以提供如下统计信息:
                hitRate():缓存命中率;
                averageLoadPenalty():加载新值的平均时间,单位为纳秒;
                evictionCount():缓存项被回收的总数,不包括显式清除。
             */
            System.out.println(testCache.stats());
    
        }
    }
    
    • 运行结果
    null
    CacheStats{hitCount=0, missCount=1, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
    

    Guava Cache 统计

    • 开启Guava Cache 的统计功能 :CacheBuilder.recordStats()
    • Cache.stats() 会返回CacheStats对象以提供以下统计信息:
    • hitRate():缓存命中率
    • averageLoadPenalty():加载新值的平均时间,单位为纳秒
    • evictionCount():缓存项被回收的总数,不包括显示清除
    • 例子见上方demo3

    如果觉得有收获就点个赞吧,更多知识,请点击关注查看我的主页信息哦~

    相关文章

      网友评论

        本文标题:Guava Cache

        本文链接:https://www.haomeiwen.com/subject/vwrmmrtx.html