• 学习使用Guava Cache


      官方文档:https://github.com/google/guava/wiki/CachesExplained

    目录

      一、guava cache介绍

      二、快速入门

        2.1、引入依赖

        2.2、第一个示例

        2.3、批量操作

      三、拓展

        3.1、移除监听器

        3.2、刷新缓存

        3.3、自定义刷新的操作

    一、guava cache介绍

      对于常见的cache(缓存),比如memecache、redis、tair这些中间件,也有jdk的一些类库可以当做缓存使用,比如实现了ConcurrentMap接口的ConcurrentHashMap。

      ConcurrentHashMap虽然可以让我们在程序中缓存一些数据,但是这些数据其实永远不会过期,除非手动删除,否则数据将一直存在于内存中。

      而guava cache(LocalCache,本地缓存),也是实现了ConcurrentMap的类,但他和ConcurrentHashMap的区别在于,guava cache可以设置数据过期以及淘汰机制,更加符合一般的应用需求。

      guava cache,是一个本地缓存,也就是说,他的数据是存放在机器内存,这个机器,就是JVM所在的机器(会占用一部分内存),而不是像redis那样专门做缓存的机器。

    二、快速入门

    2.1、引入依赖

      guava cahce是guava的子模块。

      guava仓库地址:https://mvnrepository.com/artifact/com.google.guava/guava

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

      

    2.2、第一个示例

      下面是一个简单的示例:

    package cn.ganlixin.guava;
    
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import org.junit.Test;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class UseCache {
    
        @Test
        public void testLoadingCache() throws ExecutionException {
            // 创建缓存(容器)
            LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                    // 数据写入1分钟后失效
                    .expireAfterWrite(1, TimeUnit.MINUTES)
                    // 支持缓存项的数据最大为3个
                    .maximumSize(3)
                    // 实现CacheLoader,当在缓存中未找到所需的缓存项时,会执行CacheLoader的load方法加载缓存。
                    .build(new CacheLoader<String, String>() {
                        // 方法的返回值会被缓存,注意,返回null时,会抛异常
                        @Override
                        public String load(String key) throws Exception {
                            System.out.println("key:" + key + " 未找到,开始加载....");
                            return key + "-" + key;
                        }
                    });
    
            // 加入缓存
            cache.put("Java", "spring");
            cache.put("PHP", "swoole");
    
            // 从缓存中获取值
            String res1 = cache.get("Java"); // get方法需要抛出ExecutionException异常
            // cache.getUnchecked("Java"); // 功能和get一样,但是不会throw异常
            System.out.println(res1);
            // 输出:spring
    
            // 缓存中为存放key为Golang的缓存项,所以进行加载
            String res2 = cache.get("Golang");
            System.out.println(res2);
            // key:Golang 未找到,开始加载....
            // Golang-Golang
    
            // Golang的缓存项前面已经加载过了,且没有被清除,所以这次直接获取到对应的value
            String res3 = cache.get("Golang");
            System.out.println(res3);
            // Golang-Golang
    
            // 前面添加了3个缓存项(已经达到设置上限,此时再添加缓存项,将会触发淘汰)
            cache.put("Node", "KOA");
            System.out.println(cache.get("PHP")); // PHP在cache中已被淘汰
            // key:PHP 未找到,开始加载....
            // PHP-PHP
    
            // 查询缓存,如果未找到,不会触发加载操作,而是范围null。
            String res4 = cache.getIfPresent("key");
            System.out.println(res4); // null
        }
    }  
    

      

    2.3、批量操作

      guava cache支持批量添加、查询、清除操作。

    package cn.ganlixin.guava;
    
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.collect.ImmutableMap;
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class UseCache {
    
        @Test
        public void testMultiple() throws ExecutionException {
            // 创建缓存
            LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
                    .maximumSize(2) // 最大缓存项数量为2
                    .expireAfterWrite(10, TimeUnit.SECONDS) // 数据写入10秒过期
                    .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            System.out.println("key:" + key + " 未找到,开始加载....");
                            return key + "-" + key;
                        }
                    });
    
            Map<String, String> map = new HashMap<>();
            map.put("one", "111111");
            map.put("two", "222222");
            map.put("three", "3333333");
            // 批量添加
            loadingCache.putAll(map);
    
            List<String> keys = new ArrayList<>();
            keys.add("one");
            keys.add("two");
            // 批量获取
            ImmutableMap<String, String> allData = loadingCache.getAll(keys);
            System.out.println(allData);
            // {one=one-one, two=222222}
    
            // 批量清除
            loadingCache.invalidateAll(keys);
            loadingCache.invalidateAll(); // 全量清除
        }
    }
    

      

    三、拓展

    3.1、移除监听器

      移除监听器,是指监听缓存项,当缓存项被清除时,执行指定对操作。

    package cn.ganlixin.guava;
    
    import com.google.common.cache.*;
    import org.junit.Test;
    
    import java.util.concurrent.TimeUnit;
    
    public class UseCache {
    
        @Test
        public void testRemoveListender() {
    
            LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                    .maximumSize(2)
                    .expireAfterWrite(1, TimeUnit.MINUTES)
                    // 绑定"移除监听器",当元素被清除时,执行onRemoval方法
                    .removalListener(new RemovalListener<String, String>() {
                        @Override
                        public void onRemoval(RemovalNotification removalNotification) {
                            Object key = removalNotification.getKey();
                            Object val = removalNotification.getValue();
                            System.out.println("被清除的元素,key:" + key + ", val:" + val);
    
                        }
                    })
                    // 当在缓存中未找到key时,执行load操作。
                    .build(new CacheLoader<String, String>() {
                        // 当key未找到时,执行的加载操作
                        @Override
                        public String load(String key) throws Exception {
                            System.out.println("未找到key:" + key + ",开始加载...");
                            return key + key;
                        }
                    });
    
            cache.put("one", "111111");
            cache.put("two", "123456");
    
            // 继续添加元素,会触发清理操作,触发移除监听器
            cache.put("three", "2233333");
            // 输出:被清除的元素,key:one, val:111111
    
            String res = cache.getUnchecked("four");
            System.out.println(res);
            // 输出 
            // 未找到key:four,开始加载...
            // 被清除的元素,key:two, val:123456
            // fourfour
        }
    }
    

      

    3.2、刷新缓存

      刷新缓存,是指一个缓存项写入缓存后,经过一段时间(设置的刷新间隔),再次访问该缓存项的时候,会刷新该缓存项,可以理解为将该项的key取出,执行CacheLoader的load方法,然后将返回值替换旧的值。

      可以在创建缓存的时候,设置刷新缓存的时间:

    package cn.ganlixin.guava;
    
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import org.junit.Test;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class UseCache {
    
        @Test
        public void testRefresh() throws InterruptedException, ExecutionException {
            LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                    .maximumSize(2)
                    // 设置刷新的时机
                    .refreshAfterWrite(2, TimeUnit.SECONDS)
                    .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            System.out.println("key:" + key + " 未找到,开始加载....");
                            return key + "-" + key;
                        }
                    });
    
            cache.put("one", "11111");
            cache.put("two", "22222");
    
            // 休眠3秒
            Thread.sleep(3000L);
    
            System.out.println(cache.get("one"));
            //key:one 未找到,开始加载....
            //one-one
    
            System.out.println(cache.get("two"));
            //key:two 未找到,开始加载....
            //two-two
        }
    }
    

      需要注意的是,以上面代码为例,并不是设置写入2秒后,就会被刷新,而是当写入2秒后,且再次被访问时,才会被刷新;如果一个缓存项写入的时间超过2秒,但是一直没有访问该项,那么这一项是不会被刷新的。这与Memecache和Redis的原理类似。

      

    3.3、自定义刷新缓存的操作

      前面内容中,提到可以使用设置refreshAfterWrite()设置数据写入多久后,再次被访问时,会被刷新,其实,我们可以对于刷新操作自定义,只需要重写CacheLoader的reload方法即可。

    package cn.ganlixin.guava;
    
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.util.concurrent.ListenableFuture;
    import org.junit.Test;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class UseCache {
    
        @Test
        public void testReload() throws InterruptedException {
            LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                    .maximumSize(2)
                    // 设置刷新的时机
                    .refreshAfterWrite(2, TimeUnit.SECONDS)
                    .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            System.out.println("key:" + key + " 未找到,开始加载....");
                            return key + "-" + key;
                        }
    
                        // 刷新缓存时,执行的操作
                        @Override
                        public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
                            System.out.println("刷新缓存项,key:" + key + ", oldValue:" + oldValue);
                            return super.reload(key, oldValue);
                        }
                    });
    
            cache.put("hello", "world");
            Thread.sleep(3000L);
    
            System.out.println(cache.getUnchecked("hello"));
            // 刷新缓存项,key:hello, oldValue:world
            // key:hello 未找到,开始加载....
            // hello-hello
        }
    }
    

      

     

  • 相关阅读:
    STL标准库algorithm中remove()函数的一个小注意事项
    关于 mem_fun_ref 和 bind2nd的疑问
    记录昨日程序调不通的解释
    复习几个C++概念:声明与定义、传值与拷贝构造、初始化和赋值
    对stl map 赋值添加元素的疑惑 求解(管理员让这个帖子多见会人吧~~谢谢啦!)
    摘抄书上一个逆序字符串的例子(可根据不同的符号,比如*&,.;来拆分单词)
    “指向指针的引用”小例子:忽然豁然开朗~
    论文 “tracking evolving communities in large linked networks” 中不懂的问题和知识总结
    [wp7软件]wp7~~时间日程 软件大全! 集合贴~~~
    [wp7游戏]wp7~~超级血腥类游戏~~集合贴~~
  • 原文地址:https://www.cnblogs.com/-beyond/p/12272551.html
Copyright © 2020-2023  润新知