题目
设计一个基于CompletableFuture的异步缓存系统,支持过期自动刷新和防缓存穿透
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
CompletableFuture高级用法, 并发编程, 缓存设计模式, 函数式编程, 异常处理
快速回答
实现要点:
- 使用
ConcurrentHashMap存储CompletableFuture保证原子性 - 通过
CompletableFuture.supplyAsync()异步加载数据 - 利用
ScheduledExecutorService实现定期刷新 - 采用
completeOnTimeout()处理超时降级 - 使用
Optional包装空值防止缓存穿透 - 异常处理通过
exceptionally()实现降级
核心设计原理
通过CompletableFuture的异步特性和ConcurrentHashMap的原子操作实现:
- 线程安全:
computeIfAbsent()保证单Key单线程加载 - 异步刷新:定时任务异步重建缓存,不阻塞请求
- 防穿透:空值用
Optional.empty()缓存 - 降级机制:超时/异常时返回旧值或默认值
代码实现示例
public class AsyncCache<K, V> {
private final ConcurrentHashMap<K, CompletableFuture<Optional<V>>> cache = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private final Function<K, V> loader;
private final long refreshMillis;
public AsyncCache(Function<K, V> loader, long refreshMillis) {
this.loader = loader;
this.refreshMillis = refreshMillis;
}
public CompletableFuture<Optional<V>> get(K key) {
return cache.computeIfAbsent(key, k -> {
CompletableFuture<Optional<V>> future = loadAsync(k);
scheduleRefresh(key, future);
return future;
});
}
private CompletableFuture<Optional<V>> loadAsync(K key) {
return CompletableFuture.supplyAsync(() -> {
V value = loader.apply(key);
return Optional.ofNullable(value);
})
.completeOnTimeout(Optional.empty(), 500, TimeUnit.MILLISECONDS) // 超时降级
.exceptionally(ex -> {
System.err.println("Load failed: " + ex.getMessage());
return Optional.empty(); // 异常降级
});
}
private void scheduleRefresh(K key, CompletableFuture<Optional<V>> future) {
scheduler.schedule(() -> {
if (!future.isDone() || future.isCompletedExceptionally()) return;
future.thenAcceptAsync(result -> {
if (result.isPresent()) {
cache.put(key, loadAsync(key)); // 异步刷新
}
}, scheduler);
}, refreshMillis, TimeUnit.MILLISECONDS);
}
}最佳实践
- 资源隔离:为加载任务和刷新任务使用独立线程池
- 背压控制:通过
Semaphore限制并发加载数 - 缓存清理:添加LRU机制防止内存泄漏
- 监控:记录缓存命中率/加载耗时等指标
常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
| 未处理加载异常 | Future永久异常状态 | 使用exceptionally()兜底 |
| 直接缓存null | 缓存穿透风险 | 用Optional包装空值 |
| 同步刷新阻塞 | 请求延迟飙升 | 确保刷新操作完全异步 |
| 未限制刷新并发 | 资源耗尽风险 | 添加刷新队列和限流 |
扩展知识
- 响应式整合:可返回
Mono/Flux接入Spring WebFlux - 分布式扩展:结合Redis Pub/Sub实现集群级缓存刷新
- 高级优化:
- 使用
StampedLock提升读性能 - 采用
ForkJoinPool实现工作窃取 - 添加二级缓存(Caffeine + Redis)
- 使用
- 对比框架:相比Caffeine/Guava Cache的优势:
- 更灵活的回调链(
thenCompose()等) - 直接支持响应式编程
- 更细粒度的超时控制
- 更灵活的回调链(