Caffeine缓存库的LoadingCache:缓存计算或查询结果
Caffeine 是一个高性能的 Java 缓存库,旨在提供快速、灵活和高效的缓存解决方案。
·
Caffeine 是一个高性能的 Java 缓存库,旨在提供快速、灵活和高效的缓存解决方案。常用组件有:
- LoadingCache
- CacheLoader
下面整理LoadingCache的用法:
1、Maven坐标
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.5</version> <!-- 请根据最新版本进行替换 -->
</dependency>
2、作用与使用场景
作用:
- 自动加载:当请求的键在缓存中找不到时,自动调用指定的加载函数,计算并缓存结果
- 高性能:低延迟缓存,适用于高并发的场景
- 自定义失效策略:支持基于时间、大小、引用的缓存失效策略
使用场景:
- 缓存某个复杂计算的结果,避免重复计算:如缓存使用mqadmin查询到MQ集群信息
- API调用缓存:减少API的实际调用频率
- 数据库查询结果缓存
3、常用方法
//获取指定键的值,如果不存在则加载
get(K key)
//获取指定键的值,如果存在则返回,否则返回 null
getIfPresent(K key)
//将指定的键值对放入缓存
put(K key, V value)
//从缓存中移除指定键的值
invalidate(K key)
//清空缓存
invalidateAll()
4、使用
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
public class CaffeineCacheExample {
public static void main(String[] args) {
// 创建一个 LoadingCache
LoadingCache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100) // 最大缓存条目数
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟失效
.build(key -> fetchDataFromFeignApi(key)); // 加载数据的方法
// 使用缓存
String result = cache.get("user:123"); // 若不存在,则自动加载
System.out.println(result);
// 获取已存在的值
String cachedResult = cache.getIfPresent("user:123");
System.out.println(cachedResult);
}
// 模拟从某个API获取数据
private static String fetchDataFromFeignApi(String key) {
// 实际的远程调用查询逻辑
return "Data for " + key; // 示例返回数据
}
}
当然,实际开发中,可以将LoadingCache定义成一个成员变量:
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CacheService {
/**
* 缓存对象,用于缓存API调用的结果
*/
private final LoadingCache<String, Object> apiResultCache;
/**
* Feign对象
*/
private final BasicController feignClient;
public CacheService(BasicController feignClient) {
this.apiResultCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, Object>() {
/**
* String类型的key,即你从loadingCache中查询时,传入的那个key
*/
@Override
public @Nullable Object load(String key) throws Exception {
return feignClient.feignRequest(key);
}
@Override
public @Nullable Object reload(String key, Object oldValue) throws Exception {
try {
return this.load(key); // 重新加载,失败则直接返回旧的缓存值
} catch (Exception e) {
return oldValue;
}
}
});
this.feignClient = feignClient;
}
}
如上,build中是一个CacheLoader的接口对象,可实现的方法有以下几个:
load方法是必须重写的,其余方法有默认实现,根据需要,也可以选择去异步load
5、补充
有个问题:LoadingCache作为成员变量,且高并发时,多线程一起加载数据会导致重复加载吗?验证下:
@Data
public class LoadingCacheExample {
private final LoadingCache<String, String> cache;
public LoadingCacheExample() {
cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> loadValue(key));
}
// 加载函数
private static String loadValue(String key) {
// 模拟延迟加载
try {
Thread.sleep(2000); // 2秒延迟
System.out.println(key + "被加载了一次!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Value for " + key;
}
}
测试类:启动5个线程,先去load同一个key:
public class CacheTest {
private static LoadingCacheExample cache = new LoadingCacheExample();
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
// 多个线程请求相同的key
cache.getCache().get("key");
}).start();
}
}
}
结果:
调整测试类,多个线程,去load不同的key:
public class CacheTest {
private static LoadingCacheExample cache = new LoadingCacheExample();
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
// 多个线程请求不一样的key
cache.getCache().get("key" + new Random().nextInt(100));
}).start();
}
}
}
结果:
由此可看出:将 LoadingCache 作为成员变量使用,并在高并发情况下同时请求相同的数据,LoadingCache 的设计会确保:
- 只有第一个请求该数据的线程会调用加载函数进行加载。
- 其他线程会阻塞,直到第一个请求完成,然后直接返回加载好的结果
这一效果的实现原理是:当缓存未命中时,LocalCache 会使用一个内部的 Future 对象来表示正在加载的结果,后续请求会检查这个 Future,如果它正在加载,则会调用 Future.get() 方法,这会导致线程阻塞,直到加载完成

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)