当前位置: 首页>后端>正文

Guava-CacheLoader

CacheBulider

适用性

缓存在很多场景下都是相当有用的。例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。 缓存在很多场景下都是相当有用的。例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。

Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。

通常来说,Guava Cache适用于:

  • 你愿意消耗一些内存空间来提升速度。
  • 你预料到某些键会被查询一次以上。
  • 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)

如果你的场景符合上述的每一条,Guava Cache就适合你。

如同范例代码展示的一样,Cache实例通过CacheBuilder生成器模式获取,但是自定义你的缓存才是最有趣的部分。

:如果你不需要Cache中的特性,使用ConcurrentHashMap有更好的内存效率——但Cache的大多数特性都很难基于旧有的ConcurrentMap复制,甚至根本不可能做到。

1. 基础创建CacheBuilder

    public void test2() {
        // 新建CacheBuilder
        Cache<Integer, String> cache = CacheBuilder.newBuilder().build();
        cache.put(1, "a");
        cache.put(2, "b");
        // 输出: a
        System.out.println(cache.getIfPresent(1));
        // 输出: null
        System.out.println(cache.getIfPresent(3));
        // 输出: {1=a, 2=b}
        System.out.println(cache.getAllPresent(ImmutableList.of(1,2,3)));
    }

2. 无缓存时,自定义缓存值

    public void test3() {
        // 遇到不存在的key,定义默认缓存值
        CacheLoader cacheLoader = new CacheLoader<Integer,String>() {
            @Override
            public String load(Integer key) throws Exception {
                return "缓存中找不到 key[" + key + "]对应的值";
            }
        };

        // 1. 在cache定义时设置通用缓存模版
        LoadingCache<Integer, String> cache1 = CacheBuilder.newBuilder().build(cacheLoader);
        cache1.put(1, "a");
        // 输出: a
        System.out.println(cache1.getIfPresent(1));
        try {
            // 输出: {1=a, 2=缓存中找不到 key[2]对应的值}
            System.out.println(cache1.getAll(ImmutableList.of(1,2)));
            // 输出: 缓存中找不到 key[3]对应的值
            System.out.println(cache1.get(3));
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

         // 2. 在获取缓存值时设置缓存
         Cache<Integer, String> cache2 = CacheBuilder.newBuilder().build();
         cache2.put(1, "a");
        // 输出: a
         System.out.println(cache2.getIfPresent(1));
         try {
             // 输出: key2
             String value = cache2.get(2, () -> "key2");
             System.out.println(value);
         } catch (ExecutionException e) {
             e.printStackTrace();
         }
    }

3. 控制缓存大小

    public void test4() {
        // 1. 基于缓存多少
        Cache<Integer, String> cache1 = CacheBuilder.newBuilder()
                .maximumSize(2L)  // 设置缓存上限,最多两个
                .build();
        cache1.put(1, "a");
        cache1.put(2, "b");
        cache1.put(3, "c");
        // 输出: {3=c, 2=b}
        System.out.println(cache1.asMap());
        // 输出: b
        System.out.println(cache1.getIfPresent(2));
        cache1.put(4, "d");
        // 由于key 2被调用获取过,所以key 3会被回收 输出: {2=b, 4=d}
        System.out.println(cache1.asMap());
    }

4. 控制缓存回收的时间

   public void test5() {
        // 1. 设置缓存写入后多久过期
        Cache<Integer, Integer> cache1 = CacheBuilder.newBuilder()
                // 缓存写入2s后过期
                .expireAfterWrite(2, TimeUnit.SECONDS)
                .build();
        cache1.put(1,1);
        // 输出: {1=1}
        System.out.println(cache1.asMap());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 输出: {}
        System.out.println(cache1.asMap());

        // 2. 设置缓存在给定时间内没有被读/写访问后过期
        Cache<Integer, Integer> cache2 = CacheBuilder.newBuilder()
                // 缓存读取2s后过期
                .expireAfterAccess(2, TimeUnit.SECONDS)
                .build();
        cache2.put(1,1);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cache2.getIfPresent(1);
        // 输出: {}
        System.out.println(cache2.asMap());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 输出: {}
        System.out.println(cache2.asMap());
    }

5. 手动清除缓存

    public void test6() {
        // 清除缓存中的数据
        Cache<Integer, String> cache = CacheBuilder.newBuilder().build();
        cache.put(1, "a");
        System.out.println(cache.asMap());  // 输出: {1=a}
        cache.invalidateAll();  // 清除所有缓存
        System.out.println(cache.asMap());  // 输出: {}
        cache.put(2, "b");
        System.out.println(cache.asMap());  // 输出: {2=b}
        cache.invalidate(2);  // 清除指定缓存
        System.out.println(cache.asMap());  // 输出: {}
        cache.put(1, "a");
        cache.put(2, "b");
        cache.put(3, "c");
        System.out.println(cache.asMap());  // 输出: {2=b, 1=a, 3=c}
        cache.invalidateAll(ImmutableList.of(1,2));  // 批量清除缓存
        System.out.println(cache.asMap());  // 输出: {3=c}
    }

6. 设置监听器

    public void test7() {
        // 设置移除监听器
        LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(2, TimeUnit.SECONDS)  // 设置2s后过期时间
                .removalListener(notification -> System.out.println(
                        "移除key[" + notification.getKey()
                                + "],value[" + notification.getValue()
                                + "],移除原因[" + notification.getCause() + "]")
                )  // 设置移除监听器,并设置输出模版
                .build(
                        new CacheLoader<Integer, Integer>() {
                            @Override
                            public Integer load(Integer key) throws Exception {
                                return 2;  // 当无值时, 设置默认值
                            }
                        }
                );
        cache.put(1, 1);
        cache.put(2, 2);
        System.out.println(cache.asMap());  // 输出: {2=2, 1=1}
        cache.invalidateAll();
        System.out.println(cache.asMap());  // 输出: {}
        cache.put(3, 3);
        try {
            // 如果定义的CacheLoader没有声明任何检查型异常,则可以通过getUnchecked()取值
            System.out.println(cache.getUnchecked(3));  // 输出: 3
            Thread.sleep(3000);
            System.out.println(cache.getUnchecked(3));  // 输出: 2
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

7. 统计功能

    public void test8() {
        /**
         * hitCount;  // 缓存命中数
         * missCount; // 缓存未命中数
         * loadSuccessCount; // load成功数
         * loadExceptionCount; // load异常数
         * totalLoadTime; // load的总共耗时
         * evictionCount; // 缓存项被回收的总数,不包括显式清除
         */
        // 开启统计,并查看统计信息
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .recordStats()  // 开启统计功能
                .refreshAfterWrite(2, TimeUnit.SECONDS)  // 缓存写入2s后更新
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        return UUID.randomUUID().toString();
                    }
                });
        cache.put("1", "a");
        System.out.println(cache.asMap());  // 输出: {1=a}
        System.out.println(cache.stats());  // 输出: CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
        cache.getIfPresent("2");
        System.out.println(cache.asMap());  // 输出: {1=a}
        System.out.println(cache.stats());  // 输出: CacheStats{hitCount=0, missCount=1, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //此处缓存里的值都是过期了的
        cache.getIfPresent("1");
        System.out.println(cache.asMap());  // 输出: {1=0207bb01-7b3c-4b66-b575-9fb2c5511a96}
        System.out.println(cache.stats());  // 输出: CacheStats{hitCount=1, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=21118733, evictionCount=0}
    }

https://www.xamrdz.com/backend/35k1923399.html

相关文章: