CacheBuilder本地缓存笔记

项目中应用到部分代码

	//声明本地缓存
    private static final Cache<String, List<String>> textContentCache =
            CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build();

	 //TODO 现在使用本地缓存,之后改为,如果是文本文件,可以压缩后再缓存到redis;然后拿到后解压缩(这个过程可以封装为一个方法/服务,这样可以推广使用)
    @Override
    public List getShareUrlContent(final String md5) {
        List<String> textContentList = null;
        try {
            textContentList = textContentCache.get(md5, new Callable<List<String>>() {
                @Override
                public List<String> call() throws Exception {
                    //缓存不存在,根据md5查询服务器地址,读取服务器文本内容
                    final String judgeFileUrl = getFileUrl(md5);
                    if (StringUtils.isBlank(judgeFileUrl)) {
                        return null;
                    } else {
                        final String url = com.mpen.microservice.util.FileUtils.root + judgeFileUrl;
                        final List<String> listContent = FileUtils.readLines(new File(url), "UTF-8");
                        if (CollectionUtils.isNotEmpty(listContent)) {
                            //读取内容不为空,放入缓存
                            textContentCache.put(md5, listContent);
                        }
                        return listContent;
                    }
                }
            });
        } catch (final ExecutionException e) {
            LOGGER.error("获取本地缓存方法异常:", e);
        }
        return textContentList;
    }

理解

 Cache<String, List<String>>   泛型可以改变  Cache<Object, Object> 

textContentList = textContentCache.get(md5, new Callable<List<String>>() {
          @Override
          public List<String> call() throws Exception {
					···
					函数回调
          }
      });
 -----------------------------------------------------------
 简单的理解就是:根据key查本地缓存,如果有则返回,如果没有,则执行函数方法。
 方法中就是,把内容放入缓存中

简单:

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
 .maximumSize(1000)
        .expireAfterWrite(10, TimeUnit.MINUTES) 
        .removalListener(MY_LISTENER)
        .build(
            new CacheLoader<Key, Graph>() {
                public Graph load(Key key) throws AnyException {
                    return createExpensiveGraph(key);
                }
        });


测试
maven

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>23.0</version>
</dependency>

实例

package com.cjt.demo;
 
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
 
public class CacheDemo2 {
 
    private static Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1).
            expireAfterWrite(10, TimeUnit.SECONDS).build();
 
    public static void main(String[] args) {
 
        try {
 
            Runnable runnable1 = () -> {
                for (int i = 0; i < 10; i++) {
                    try {
                        String str = cache.get("123", new Callable<String>() {
                            public String call() throws Exception {
                                cache.put("123","123");
                                return "111111111111111";
                            }
                        });
                        System.out.println(str);
 
                        Thread.currentThread().sleep(1000);
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Runnable runnable2 = () -> {
                for (int i = 0; i < 10; i++) {
                    try {
                        System.out.println(cache.get("456", new Callable<String>() {
                            public String call() throws Exception {
                                cache.put("456","456");
                                return "222222222222222";
                            }
                        }));
                        Thread.currentThread().sleep(1000);
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Thread thread1 = new Thread(runnable1);
            Thread thread2 = new Thread(runnable2);
            thread1.start();
            thread2.start();
 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三种基于时间清理或刷新缓存数据的方式:
expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。

expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收(移除key),需要等待获取新值才会返回。

refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。第一个请求进来,执行load把数据加载到内存中(同步过程),指定的过期时间内比如10秒,都是从cache里读取数据。过了10秒后,没有请求进来,不会移除key。再有请求过来,才则执行reload,在后台异步刷新的过程中,如果当前是刷新状态,访问到的是旧值。刷新过程中只有一个线程在执行刷新操作,不会出现多个线程同时刷新同一个key的缓存。在吞吐量很低的情况下,如很长一段时间内没有请求,再次请求有可能会得到一个旧值(这个旧值可能来自于很长时间之前),这将会引发问题。(可以使用expireAfterWrite和refreshAfterWrite搭配使用解决这个问题)

原文链接


版权声明:本文为YSPLX原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。