guava缓存的使用

357 阅读2分钟

guava缓存的使用

1.基础-Cache直接存取

普通的key-value缓存类似Map的使用:

    public static void main(String[] args) throws Exception {
        //创建一个1000长度的缓存,10毫秒失效
        Cache<String, Stu> cache = CacheBuilder
                .newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MILLISECONDS)
                .build();
        //放入一个数据,key为1
        cache.put("1", new Stu("name1"));
        //放入后直接取,有值
        System.out.println("放入之后getIfPresent:" + cache.getIfPresent("1"));
        Thread.sleep(11);
        //休息11秒再取,已经失效,无值
        System.out.println("休息11毫秒,失效后getIfPresent:" + cache.getIfPresent("1"));
    }

    private static record Stu(String name) {
    }

输出如下,符合预期

放入之后getIfPresent:Stu[name=name1]
休息11毫秒,失效后getIfPresent:null

2.基础-Cache并发重复加载

guava中不存在,触发回源(可能是Redis或库),并发场景中,同一个数据会多次回源:

    //创建一个1000长度的缓存,1秒失效
    private static Cache<Long, Stu> skuCache = CacheBuilder.newBuilder().maximumSize(1000)
            .expireAfterWrite(1000, TimeUnit.MILLISECONDS).build();

    private static Stu getStu(Long id) throws Exception {
        Stu stu = skuCache.getIfPresent(id);
        if (null != stu) {
            return stu;
        }
        //模拟回源加载数据需要时间,回源加载数据并放入缓存
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stu = new Stu(id, "name" + id);
        skuCache.put(id, stu);
        System.out.println("回源加载:" + id);
        return stu;
    }

    public static void main(String[] args) throws Exception {
        //三个线程并发请求同一个数据,会加载3次
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println(getStu(1L));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static record Stu(Long id, String name) {
    }

执行输出如下,同样的数据name1在三个线程中被重复加载3次。

回源加载:1
回源加载:1
回源加载:1
Stu{name='name1'}
Stu{name='name1'}
Stu{name='name1'}

3.基础-通过LoadingCache来避免并发重复加载

代码如下:

    //创建一个1000长度的缓存,1秒失效
    private static LoadingCache<Long, Stu> cacheLoad = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(30, TimeUnit.MILLISECONDS)
            .build(new CacheLoader<Long, Stu>() {
                @Override
                public Stu load(Long id) throws Exception {
                    //模拟回源加载数据需要时间
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("回源加载:" + id);
                    return new Stu(id, "name" + id);
                }
            });

    public static void main(String[] args) throws Exception {
        //三个线程并发请求同一个数据,只会加载1次
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println(cacheLoad.get(1L));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

执行结果如下,只回源了一次:

回源加载:1
Stu[id=1, name=name1]
Stu[id=1, name=name1]
Stu[id=1, name=name1]