一、核心定位与简介

1.1 什么是 Caffeine

Caffeine 是一款基于 Java 8 开发的高性能本地缓存框架,是目前 Java 生态中性能最优的本地缓存实现,由 Guava Cache 原核心作者参与研发,属于 Guava Cache 的升级迭代版本。

Caffeine 吸收了 Guava Cache 的核心设计思想,结合 Java 8 的 Lambda 表达式、CompletableFuture 异步特性做了深度优化,同时采用当前业界最优的缓存淘汰算法,是 Java 项目中本地缓存的首选方案。

1.2 核心优势

  • 极致的读写性能:读操作性能接近原生 ConcurrentHashMap,写操作性能远超同类框架
  • 超高的缓存命中率:自研 W-TinyLFU 淘汰算法,命中率远高于 Guava Cache、Ehcache 等
  • 轻量级无依赖:核心包体积小巧,无第三方依赖,兼容所有 Java 8+ 版本项目
  • 丰富的功能特性:支持多种加载模式、过期策略、异步操作、缓存统计等
  • 规范兼容:完美兼容 JSR-107 缓存规范,适配标准化缓存开发
  • 内存友好:支持基于 Java 引用的内存回收策略,避免内存溢出

二、核心底层原理

2.1 核心缓存淘汰算法:W-TinyLFU

Caffeine 超高命中率的核心是 W-TinyLFU(Window-TinyLFU) 算法,这是一种复合型缓存淘汰策略,融合经典算法优势并解决其痛点:

  • 核心设计思想:结合 LFU(最少频率使用)的高频访问优先 + LRU(最近最少使用)的近期访问优先,兼顾「访问频率」与「访问时效性」
  • 解决 LRU 痛点:避免「缓存污染」(低频大流量请求挤出高频数据)
  • 解决 LFU 痛点:避免「冷启动」(新热点数据无法缓存)和「数据僵化」(历史高频数据长期占用缓存)
  • 核心优化:通过窗口缓存预热新数据访问频率,结合频率过滤机制,留存高频数据、纳入新热点、剔除低频数据

2.2 核心执行机制

  • 存储实现:基于分段式并发哈希表,与 ConcurrentHashMap 分段锁思路一致,读写无锁化设计,降低线程竞争损耗
  • 过期清理:采用「惰性删除 + 定时主动清理」组合机制,兼顾性能与内存占用
  • 加载逻辑:基于 Java 8 函数式编程实现,支持同步/异步模式,异步操作依托 CompletableFuture 提升并发效率

三、核心缓存策略

3.1 缓存数据淘汰策略

Caffeine 淘汰策略分两类,自动执行无需手动干预:

  • 被动淘汰:缓存容量达预设最大值时,触发 W-TinyLFU 算法驱逐「价值最低」数据
  • 主动淘汰:缓存数据满足过期规则时自动移除,触发时机包括惰性检查、定时清理

3.2 三大核心过期策略

3.2.1 基于大小的过期(容量驱逐)

设置缓存最大容量上限,达到上限时触发 W-TinyLFU 淘汰,支持最大条目数/自定义权重配置,是防止内存溢出的核心配置:

  • maximumSize(最大条目数):相当于给缓存设置「最多能放多少个东西」,不管每个东西大小,只要数量到上限就踢掉最没用的。
    比如设置 maximumSize(1000),缓存里放了1000个键值对(哪怕其中有1KB的小数据,也有100MB的大数据),就会触发驱逐。
  • 权重(maximumWeight + weigher):相当于给缓存设置「最多能装多少容量的东西」,每个东西先算「重量」,总重量到上限就踢掉最没用的。
    比如设置 maximumWeight(10240)(总权重10KB),给每个字符串缓存项按长度算权重(weigher((k,v)->v.length())),当所有字符串总长度≥10KB时,触发驱逐。

配置示例:

// 配置最大条目数
Cache<String, String> cache = Caffeine.newBuilder()
        .maximumSize(1000) // 最大缓存条目数
        .build();

// 配置自定义权重
LoadingCache<String, String> weightCache = Caffeine.newBuilder()
        .maximumWeight(10240) // 总权重上限
        .weigher((key, value) -> value.length()) // 自定义每个缓存项的权重
        .build(key -> loadData(key));

3.2.2 基于时间的过期(时效驱逐)

业务开发最常用策略,支持两种细分规则:

  • 访问后过期(TTL):最后一次访问开始计时,超时过期,适用于热点时效性数据(如用户会话)
  • 写入后过期(TTI):首次写入开始计时,超时过期,适用于固定更新频率数据(如配置信息)

配置示例:

LoadingCache<String, String> timeCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterAccess(5, TimeUnit.MINUTES) // TTL:访问后5分钟过期
        .expireAfterWrite(10, TimeUnit.MINUTES) // TTI:写入后10分钟过期
        .build(key -> loadData(key));

3.2.3 基于引用的过期(内存感知驱逐)

基于 Java 引用类型实现内存级回收,对接 JVM 垃圾回收机制:

  • 软引用策略:JVM 内存不足时回收缓存,避免内存溢出
  • 弱引用策略:键对象无引用时立即回收缓存,适配对象生命周期绑定场景

配置示例:

// 软引用值
Cache<String, String> softCache = Caffeine.newBuilder()
        .softValues()
        .build();

// 弱引用键
Cache<String, String> weakCache = Caffeine.newBuilder()
        .weakKeys()
        .build();

四、核心缓存工作模式

核心特性:缓存增删改查完全手动控制,缓存缺失时框架不自动处理
适用场景:少量固定数据缓存、需精准控制更新时机的场景

示例代码:

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Cache;
import java.util.concurrent.TimeUnit;

public class ManualLoadExample {
    public static void main(String[] args) {
        // 构建手动加载缓存
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
        
        // 手动存入
        cache.put("user:1001", "张三");
        // 手动获取
        String userName = cache.getIfPresent("user:1001");
        // 手动删除
        cache.invalidate("user:1001");
    }
}

4.2 自动加载模式

核心特性:配置加载逻辑,缓存缺失时框架自动加载并写入缓存
适用场景:绝大多数查询类业务,是 Caffeine 最常用模式

示例代码:

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.util.concurrent.TimeUnit;

public class AutoLoadExample {
    public static void main(String[] args) {
        // 构建自动加载缓存
        LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterAccess(10, TimeUnit.MINUTES)
                .build(key -> loadDataFromDB(key)); // 缓存缺失时自动执行
        
        // 获取缓存(缺失则自动加载)
        String productName = loadingCache.get("product:2001");
    }

    // 模拟从数据库加载数据
    private static String loadDataFromDB(String key) {
        System.out.println("从数据库加载数据:" + key);
        return "商品_" + key.split(":")[1];
    }
}

4.3 异步手动加载模式

核心特性:基于 CompletableFuture 实现异步非阻塞操作,提升高并发响应效率
适用场景:高并发、对响应时间要求严苛的场景

示例代码:

import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class AsyncManualLoadExample {
    public static void main(String[] args) throws Exception {
        AsyncCache<String, String> asyncCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .buildAsync();
        
        // 异步存入
        asyncCache.put("order:3001", CompletableFuture.supplyAsync(() -> "订单_3001"));
        // 异步获取
        CompletableFuture<String> orderFuture = asyncCache.getIfPresent("order:3001");
        String orderInfo = orderFuture.get(); // 阻塞获取结果(实际业务中建议链式调用)
    }
}

4.4 异步自动加载模式

核心特性:融合自动加载与异步加载优势,终极高性能模式,缓存缺失时异步加载,主线程不阻塞
适用场景:高并发+高频查询的核心业务场景

示例代码:

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class AsyncAutoLoadExample {
    public static void main(String[] args) throws Exception {
        AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .buildAsync(key -> CompletableFuture.supplyAsync(() -> loadDataFromDBAsync(key)));
        
        // 异步获取,主线程不阻塞
        CompletableFuture<String> dataFuture = asyncLoadingCache.get("goods:4001");
        // 链式处理结果(非阻塞)
        dataFuture.thenAccept(data -> System.out.println("加载结果:" + data));
    }

    private static String loadDataFromDBAsync(String key) {
        try {
            // 模拟耗时加载
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "异步加载_" + key;
    }
}

五、核心功能特性

5.1 高性能并发控制

  • 底层无锁化并发设计,支持数万级并发读写,线程安全无锁竞争损耗
  • 支持自定义并发级别,适配不同系统负载,兼顾性能与内存占用

5.2 缓存刷新机制

缓存刷新是未过期时异步更新数据(与过期重新加载不同),刷新期间返回旧值,保证响应效率与数据最终一致性,刷新机制是惰性刷新,只有key被访问才会被触发:

LoadingCache<String, String> refreshCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(30, TimeUnit.MINUTES)
        // 每次命中检查key有效时间<=30-5分钟时异步刷新一次
        .refreshAfterWrite(5, TimeUnit.MINUTES) 
        .build(key -> loadDataFromDB(key));

5.3 缓存淘汰监听器

监听缓存淘汰/过期/删除事件,支持同步/异步回调,用于统计、日志、资源释放:

Cache<String, String> listenerCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .removalListener((key, value, cause) -> {
            // cause枚举:EXPLICIT(手动删除)、EXPIRED(过期)、SIZE(容量驱逐)
            System.out.printf("缓存移除 - 键:%s,原因:%s%n", key, cause.name());
        })
        .build();

5.4 完整的缓存统计功能

开启后可获取命中率、加载时间、驱逐数等核心指标,用于性能调优:

LoadingCache<String, String> statsCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .recordStats() // 开启统计
        .build(key -> loadDataFromDB(key));

// 获取核心统计指标
System.out.println("命中率:" + statsCache.stats().hitRate());
System.out.println("驱逐次数:" + statsCache.stats().evictionCount());
System.out.println("平均加载时间:" + statsCache.stats().averageLoadTime() + "ns");

5.5 高并发场景配置

Caffeine 底层基于分段式哈希表实现(类似 ConcurrentHashMap),并发级别对应「哈希表的分段数」,Caffeine 提供 concurrencyLevel(int) 方法显式设置并发级别,默认是16(仅在以下特殊场景需要调整),如果缓存加载逻辑使用自定义线程池,建议并发级别与线程池大小匹配,代码示例如下:

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class CaffeineConcurrencyWithPool {
    public static void main(String[] args) {
        // 自定义线程池(8 核心)
        var executor = Executors.newFixedThreadPool(8);
        
        // 并发级别设为 8(与线程池大小一致,且为 2 的幂次方)
        LoadingCache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                // 设置并发级别为8
                .concurrencyLevel(8)
                .executor(executor) // 绑定自定义线程池
                .build(key -> loadDataFromDB(key));
    }

    private static String loadDataFromDB(String key) {
        return "data_" + key;
    }
}

5.5 其他核心特性

  • 禁止缓存空值:避免缓存穿透,可配置过滤空值/无效值
  • 缓存预热:项目启动时加载热点数据,避免初期缓存缺失
  • 自定义键值配置:支持自定义 equals/hashCode、序列化规则,适配自定义对象键

六、与主流本地缓存框架对比

维度 Caffeine Guava Cache Ehcache 3.x ConcurrentHashMap
读写性能 极致(最优) 良好 一般 读优写一般
缓存命中率 最高 中等 中等 无淘汰策略
功能完备性 丰富 基础 极丰富(企业级) 极简
轻量化 最优 优秀 较重 最优
分布式支持 不支持(本地) 不支持(本地) 支持 不支持
持久化支持 不支持 不支持 支持 不支持

核心结论

  • 纯内存本地缓存:优先选 Caffeine
  • 需持久化/分布式:选 Ehcache 3.x
  • Java 7 项目:选 Guava Cache
  • 简单临时存储:选 ConcurrentHashMap

七、最佳实践与避坑指南

7.1 核心配置优化原则

  1. 容量:设置为热点数据量的 1.5-2 倍,避免频繁驱逐/内存浪费
  2. 过期时间:热点数据用 TTL(访问后过期),静态数据用 TTI(写入后过期),兼顾命中率与一致性
  3. 引用策略:内存敏感业务用软引用,普通业务用默认强引用
  4. 统计:必开统计功能,通过指标优化配置
  5. 并发级别:使用默认值可满足99%的高并发场景

7.2 业务场景最佳适配

  • 普通查询:自动加载模式 + TTL 过期
  • 高并发核心业务:异步自动加载 + 容量驱逐 + TTL
  • 内存敏感业务:软引用 + 容量驱逐
  • 高频更新业务:手动加载 + TTI 过期

7.3 核心避坑指南

  • 混淆刷新与过期:刷新是异步更新,过期是重新加载,按需选择
  • 盲目设置过期时间:静态数据无需过期,动态数据必须设置
  • 缓存空值:无特殊需求时禁止缓存空值,避免内存浪费
  • 可变对象做键:保证键不可变或重写 equals/hashCode
  • 过度依赖缓存:缓存仅为性能优化手段,需保证缓存缺失时业务正常
  • 忽略统计:通过命中率调整配置(命中率<80%需优化容量/过期时间)

八、适用与不适用场景

8.1 核心适用场景

  • 高频访问的本地热点数据缓存(配置、用户信息、商品详情)
  • 分布式缓存(Redis)的一级缓存,降低分布式缓存压力
  • 高并发无状态缓存(限流、熔断规则)
  • 内存敏感型业务缓存
  • 异步编程的缓存需求

8.2 不适用场景

  • 需要持久化存储的缓存(选 Ehcache/Redis)
  • 需要分布式一致性的缓存(选 Redis/Memcached)
  • 超大容量缓存存储(选分布式缓存)
  • 跨 JVM 共享缓存数据(选分布式缓存)

九、总结

9.1 核心总结

  1. Caffeine 是 Java 8+ 最优本地缓存框架,W-TinyLFU 算法保证超高命中率,性能极致
  2. 核心优势是高性能、高命中率、轻量级、功能完备,适配所有本地缓存需求
  3. 四种工作模式 + 三种过期策略可灵活组合,自动/异步加载是核心亮点
  4. 「Caffeine + Redis」双层缓存架构是微服务最优缓存方案

9.2 最终建议

  1. Java 本地缓存优先选择 Caffeine,远超同类框架
  2. 配置遵循「合理容量、按需过期、开启统计」原则,持续优化
  3. 明确缓存定位为「性能优化手段」,保证业务容错能力
  4. 避开常见坑点,最大化发挥 Caffeine 性能优势

Caffeine 是 Java 本地缓存的事实标准,掌握其核心特性与最佳实践,能显著提升项目并发性能与开发效率,是 Java 开发人员必备技术栈。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐