项目中应用到部分代码
//声明本地缓存
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版权协议,转载请附上原文出处链接和本声明。