loadingcache优化
问题分析
通过当前现场的火焰图进行分析
原本的loadingcache
public LoadingCache<Integer, Student> map = Caffeine.newBuilder()
.refreshAfterWrite(CONTRACT_CACHE_HOURS, TimeUnit.HOURS)
.maximumSize(CONTRACT_CONFIG_CACHE_SIZE)
.recordStats().build(
key -> {
try {
Student condition = new Student();
condition.setKey(key);
List<Student> Students = studentMapper.selectBySelective(condition);
return Students.stream().findFirst().orElse(null);
} catch (Exception e) {
//
}
return null;
}
);
调用点
@Override
public Student getStudentByKey(Integer key) {
if (null == key) {
return null;
}
return map.get(key);
}
原理分析,get在并发时会出现锁竞争
代码优化
查阅官方文档推荐使用buildAsync,替换掉get和build,修改后的代码如下
private static final ExecutorService CACHE_REFRESH_EXECUTOR = Executors.newFixedThreadPool(8);
public AsyncLoadingCache<Integer, Student> map =
Caffeine.newBuilder()
.refreshAfterWrite(CONTRACT_CACHE_HOURS, TimeUnit.HOURS)
.maximumSize(CONTRACT_CONFIG_CACHE_SIZE)
.recordStats()
.buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> {
try {
Student condition = new Student();
condition.setKey(key);
List<Student> students = studentMapper.selectBySelective(condition);
return students.stream().findFirst().orElse(null);
} catch (Exception e) {
//
}
return null;
}, CACHE_REFRESH_EXECUTOR));
AsyncLoadingCache的get是一个CompletableFuture,是异步加载
对比build和buildAsync的不同
通过源码可以看到buildAsync的cacheloader是一个AsyncCacheLoader
所以build在构造loadingcache的时候传入的是同步cacheloader,所以get就会出现锁等待;但是buildAsync在构造loadingcache的时候传入的是异步AsyncCacheLoader,所以get也是异步。