Java Caffeine 本地缓存 作者:马育民 • 2026-04-16 18:36 • 阅读:10001 ## 介绍 Caffeine 是一个基于 Java 8 开发的高性能本地缓存库,由 Ben Manes 开发,旨在作为 Google Guava Cache 的现代化替代品。它凭借极致的性能和超高的缓存命中率,被广泛应用于 Spring Boot、Hibernate 等主流框架中,被誉为 Java 本地缓存的首选方案。 ### 🚀 核心优势:为何选择 Caffeine? 与 Guava Cache 或 `ConcurrentHashMap` 相比,Caffeine 在性能和功能上都有显著优势: * **极致的读写性能**:通过高度优化的并发控制,其读写吞吐量远超同类框架,读操作性能接近原生的 `ConcurrentHashMap`。 * **超高的缓存命中率**:核心在于其先进的缓存淘汰算法,能有效识别并保留热点数据。 * **丰富的功能特性**:支持多种过期策略、异步加载、缓存统计等,功能完备且配置灵活。 * **轻量级无依赖**:核心包体积小,无任何第三方依赖,可轻松集成到项目中。 以下是 Caffeine 与 Guava Cache 的核心特性对比: | 特性 | Caffeine | Guava Cache | | :--- | :--- | :--- | | **淘汰算法** | ✅ Window TinyLFU (命中率更高) | LRU 近似 | | **性能** | ⚡ 极高 (接近理论最优) | 良好 | | **异步加载** | ✅ `AsyncLoadingCache` | ❌ | | **社区活跃度** | 🌟 持续更新 | 🛑 维护模式 | ### 🧠 核心原理:W-TinyLFU 算法 Caffeine 高性能的灵魂在于其采用的 **W-TinyLFU (Window TinyLFU)** 缓存淘汰算法。该算法巧妙地结合了 LFU (Least Frequently Used) 和 LRU (Least Recently Used) 的优点,解决了传统算法的痛点: 1. **解决 LRU 的“缓存污染”问题**:LRU 仅关注数据的最近访问时间,当有大量低频数据突然涌入时,容易将真正的热点数据挤出缓存。 2. **解决 LFU 的“数据僵化”问题**:LFU 仅关注数据的访问频率,导致一些历史上高频但当前已不再访问的数据长期驻留缓存,无法被淘汰。 W-TinyLFU 通过“窗口缓存”对新数据进行频率预热,再结合频率过滤机制,既能快速接纳新的热点数据,又能有效剔除低频无效数据,从而在各种访问模式下都能保持极高的缓存命中率。 ### 🛠️ 核心特性详解 Caffeine 提供了丰富且强大的功能,以满足不同业务场景的需求。 #### 灵活的驱逐与过期策略 Caffeine 支持多种策略来自动管理缓存空间,防止内存溢出。 * **基于容量驱逐**:当缓存条目数量达到预设最大值时,自动淘汰“价值最低”的数据。 ```java .maximumSize(10_000) // 最多缓存 10,000 个条目 ``` * **基于权重驱逐**:可以为每个缓存条目定义权重(如对象大小),当总权重超限时触发驱逐。 ```java .maximumWeight(100_000) .weigher((String key, Data value) -> value.sizeInBytes()) ``` * **基于时间过期**: * `expireAfterWrite`: 写入后固定时间过期。 * `expireAfterAccess`: 最后一次访问后固定时间过期。 * `expireAfter`: 支持自定义每个条目的过期时间。 #### 多样的缓存加载模式 * **手动加载 (`Cache`)**:适用于需要显式 `put`/`get` 的场景。 ```java Cache cache = Caffeine.newBuilder().build(); cache.put("key", "value"); Object val = cache.getIfPresent("key"); ``` * **同步加载 (`LoadingCache`)**:当查询的键不存在时,自动调用加载函数来获取数据,避免缓存穿透。 ```java LoadingCache cache = Caffeine.newBuilder() .build(key -> fetchDataFromDB(key)); String value = cache.get("userId"); // 若缓存未命中,自动调用加载函数 ``` * **异步加载 (`AsyncLoadingCache`)**:基于 `CompletableFuture` 实现,在高并发场景下不阻塞调用线程,是性能最佳实践。 ```java AsyncLoadingCache cache = Caffeine.newBuilder() .buildAsync(key -> CompletableFuture.supplyAsync(() -> fetchDataFromDB(key))); CompletableFuture future = cache.get("userId"); ``` #### 其他实用功能 * **缓存统计 (`recordStats`)**:开启后可以获取缓存命中率、加载时间、驱逐次数等关键指标,便于监控和调优。 * **移除监听器 (`removalListener`)**:当数据因驱逐、过期或手动删除而被移除时,可以注册监听器接收通知。 * **引用类型 (`weakKeys`/`softValues`)**:支持弱引用或软引用,当 JVM 内存不足时,GC 可以回收这些对象,有效防止内存泄漏。 # 使用 ### **添加 Maven 依赖** ```xml com.github.ben-manes.caffeine caffeine 3.2.0 ``` ### **创建并使用缓存** ```java import com.github.benmanes.caffeine.cache.Cache; import java.util.concurrent.TimeUnit; public class CaffeineExample { public static void main(String[] args) { // 构建一个缓存 Cache cache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(5, TimeUnit.MINUTES) .recordStats() .build(); // 存入数据 cache.put("key1", "value1"); // 获取数据 String value = cache.getIfPresent("key1"); System.out.println(value); // 输出: value1 // 获取或计算 (如果不存在则执行函数并放入缓存) String value2 = cache.get("key2", k -> "computed-" + k); System.out.println(value2); // 输出: computed-key2 // 查看统计信息 System.out.println(cache.stats()); } } ``` 原文出处:http://malaoshi.top/show_1GW38uq6z6cW.html