一文详解Caffeine缓存库的常用功能与使用指南

 更新时间:2025年07月31日 08:56:20   作者:越重天  
Caffeine作为新一代高性能Java缓存库,在并发场景下展现出卓越表现,本文将介绍Caffeine缓存库的常用功能使用介绍,希望对大家有所帮助

Caffeine作为新一代高性能Java缓存库,在并发场景下展现出卓越表现。它通过创新的W-TinyLFU淘汰算法实现高达99%的命中率,并采用无锁设计使吞吐量较传统方案提升5-10倍。该库提供灵活的缓存管理能力:支持基于时间(写入/访问过期)、数量或权重的淘汰策略;允许为单个Key设置专属过期时间;独创的异步刷新机制能在不阻塞请求的情况下更新数据。开发者可通过简洁的链式API配置内存控制、加载逻辑和事件监听,轻松构建高并发低延迟的智能缓存系统。其与Guava Cache兼容的接口设计,更使迁移成本降至最低。

以下是 Caffeine 缓存库的常用功能使用介绍,涵盖基础操作、过期策略、淘汰配置等核心功能:

一、基础缓存操作

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

// 1. 创建缓存实例
Cache<String, Object> cache = Caffeine.newBuilder()
        .build();

// 2. 添加数据
cache.put("key1", "value1");

// 3. 获取数据(手动)
Object value = cache.getIfPresent("key1"); // 存在返回value,否则null

// 4. 删除数据
cache.invalidate("key1");
cache.invalidateAll(); // 清空缓存

二、自动加载缓存(推荐)

LoadingCache<String, Object> cache = Caffeine.newBuilder()
        .build(key -> {
            // 缓存未命中时自动执行的加载逻辑
            return fetchFromDB(key); // 自定义数据库加载方法
        });

// 自动加载数据(缓存未命中时执行build中的逻辑)
Object value = cache.get("user_123");

三、过期策略配置

1. 全局过期策略

Cache<String, Object> cache = Caffeine.newBuilder()
        // 写入后30分钟过期
        .expireAfterWrite(30, TimeUnit.MINUTES)
        
        // 最后访问后15分钟过期
        .expireAfterAccess(15, TimeUnit.MINUTES)
        
        // 自定义过期策略(按需实现)
        .expireAfter(new Expiry<String, Object>() {
            public long expireAfterCreate(String key, Object value, long currentTime) {
                return TimeUnit.MINUTES.toNanos(10); // 创建后10分钟过期
            }
            public long expireAfterUpdate(String key, Object value, long currentTime, long currentDuration) {
                return currentDuration; // 更新后不改变过期时间
            }
            public long expireAfterRead(String key, Object value, long currentTime, long currentDuration) {
                return currentDuration; // 读取后不改变过期时间
            }
        })
        .build();

2. 单Key过期(高级用法)

// 创建支持变长过期的缓存
Cache<String, Object> cache = Caffeine.newBuilder()
        .expireAfter(new Expiry<String, Object>() {
            // ...实现同上
        })
        .build();

// 为特定Key设置不同过期时间
cache.policy().expireVariably().ifPresent(policy -> {
    policy.put("hot_key", "value", 2, TimeUnit.HOURS);  // 2小时
    policy.put("cold_key", "value", 10, TimeUnit.MINUTES); // 10分钟
});

四、淘汰策略配置

Cache<String, Object> cache = Caffeine.newBuilder()
        // 基于数量淘汰(最多1000个条目)
        .maximumSize(1000)
        
        // 基于权重淘汰(需实现Weigher)
        .maximumWeight(10_000)
        .weigher((String key, Object value) -> {
            // 自定义权重计算逻辑
            if (value instanceof String) return ((String) value).length();
            if (value instanceof List) return ((List<?>) value).size();
            return 1;
        })
        
        // 基于引用回收(谨慎使用)
        .weakKeys()      // 弱引用Key
        .weakValues()    // 弱引用Value
        .softValues()    // 软引用Value
        .build();

五、刷新策略(优于纯过期)

LoadingCache<String, Object> cache = Caffeine.newBuilder()
        // 写入后1分钟可刷新(不阻塞读取,异步刷新旧值)
        .refreshAfterWrite(1, TimeUnit.MINUTES)
        .build(key -> fetchFromDB(key));

六、监听器与统计

Cache<String, Object> cache = Caffeine.newBuilder()
        // 移除监听器
        .removalListener((String key, Object value, RemovalCause cause) -> {
            System.out.printf("Key %s was removed (%s)%n", key, cause);
        })
        // 启用统计
        .recordStats()
        .build();

// 获取统计信息
CacheStats stats = cache.stats();
System.out.printf("Hit Rate: %.2f%%, Loads: %d%n",
        stats.hitRate() * 100, stats.loadCount());

七、异步操作

// 1. 异步缓存
AsyncLoadingCache<String, Object> asyncCache = Caffeine.newBuilder()
        .buildAsync(key -> fetchFromDB(key));

// 获取数据(返回CompletableFuture)
CompletableFuture<Object> future = asyncCache.get("key1");

// 2. 同步视图操作
Object value = asyncCache.synchronous().get("key1");

八、最佳实践配置模板

LoadingCache<String, Object> optimalCache = Caffeine.newBuilder()
        // 容量控制
        .maximumSize(10_000)
        
        // 过期策略
        .expireAfterWrite(30, TimeUnit.MINUTES)
        
        // 刷新策略
        .refreshAfterWrite(5, TimeUnit.MINUTES)
        
        // 统计和监听
        .recordStats()
        .removalListener((key, value, cause) -> 
            logRemoval(key, cause))
        
        // 自动加载
        .build(key -> fetchFromDB(key));

九、基于Caffeine实现的动态缓存

我们有时候需要这样一种场景:当用户请求某个key的时候,该缓存自动从数据库去加载,就是注册一个数据库加载器(自己实现),当获取不到该key时,自动走数据库查询,然后存入该key中。当往caffeine缓存中插入一个key后,如果缓存没有,则自动存入,并自动同步到数据库中,当删除一个key,或key过期后,自动从数据库同步删除。

以下是简单的实现流程:

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

public class DynamicCache<K, V> {

    private final Cache<K, V> cache;
    private final DataLoader<K, V> dataLoader;
    private final DataSynchronizer<K, V> dataSynchronizer;

    public DynamicCache(DataLoader<K, V> dataLoader, DataSynchronizer<K, V> dataSynchronizer) {
        this.dataLoader = dataLoader;
        this.dataSynchronizer = dataSynchronizer;
        
        this.cache = Caffeine.newBuilder()
                // 配置缓存策略(按需设置)
                .expireAfterWrite(30, TimeUnit.MINUTES)  // 30分钟过期
                .maximumSize(1000)                      // 最大缓存项
                // 注册移除监听器(用于删除数据库数据)
                .removalListener((K key, V value, RemovalCause cause) -> {
                    if (cause.wasEvicted()) {  // 仅处理过期或容量剔除
                        dataSynchronizer.deleteFromDatabase(key);
                    }
                })
                // 注册加载器(用于缓存未命中时从DB加载)
                .build(key -> {
                    V value = dataLoader.loadFromDatabase(key);
                    if (value == null) throw new Exception("Key not found");
                    return value;
                });
    }

    // 获取数据(自动加载)
    public V get(K key) {
        return cache.get(key, k -> {
            V value = dataLoader.loadFromDatabase(k);
            if (value == null) throw new RuntimeException("Data not found");
            return value;
        });
    }

    // 添加/更新数据(同步到数据库)
    public void put(K key, V value) {
        // 先同步到数据库
        dataSynchronizer.saveToDatabase(key, value);
        // 再更新缓存
        cache.put(key, value);
    }

    // 删除数据(同步删除数据库)
    public void delete(K key) {
        // 先删除数据库数据
        dataSynchronizer.deleteFromDatabase(key);
        // 再使缓存失效
        cache.invalidate(key);
    }

    // 数据库加载器接口
    public interface DataLoader<K, V> {
        V loadFromDatabase(K key);
    }

    // 数据库同步器接口
    public interface DataSynchronizer<K, V> {
        void saveToDatabase(K key, V value);
        void deleteFromDatabase(K key);
    }
}

上述接口使用示例:

// 1. 实现数据库操作接口
DynamicCache.DataLoader<String, User> loader = key -> 
    jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", User.class, key);

DynamicCache.DataSynchronizer<String, User> synchronizer = new DynamicCache.DataSynchronizer<>() {
    @Override
    public void saveToDatabase(String key, User value) {
        jdbcTemplate.update("INSERT OR REPLACE INTO users (id, name) VALUES (?, ?)", 
                           key, value.getName());
    }

    @Override
    public void deleteFromDatabase(String key) {
        jdbcTemplate.update("DELETE FROM users WHERE id=?", key);
    }
};

// 2. 创建缓存实例
DynamicCache<String, User> userCache = new DynamicCache<>(loader, synchronizer);

// 3. 使用缓存
// 自动加载(缓存未命中时从DB加载)
User user = userCache.get("user123");  

// 添加/更新(同步到DB)
userCache.put("user456", new User("Alice"));

// 删除(同步删除DB数据)
userCache.delete("user789");

关键特性说明:

1.自动加载

  • 当调用 get() 方法且缓存未命中时,自动通过 DataLoader 从数据库加载
  • 加载成功后自动填充缓存

2.写穿透

put() 操作时:

  • 先通过 DataSynchronizer 保存到数据库
  • 再更新缓存

保证数据库与缓存的数据一致性

3.删除同步

delete() 操作时:

  • 先删除数据库数据
  • 再使缓存失效

缓存过期/淘汰时:

通过 RemovalListener 自动触发数据库删除

4.缓存配置

  • 可自定义过期时间(expireAfterWrite
  • 可设置最大容量(maximumSize
  • 支持其他Caffeine特性(刷新策略、弱引用等)

关键特性对比表

功能配置方法适用场景
写入过期expireAfterWrite()数据更新频率低的场景
访问过期expireAfterAccess()读多写少的场景
自适应过期expireAfter(Expiry)需要动态过期时间的场景
数量淘汰maximumSize()通用场景
权重淘汰maximumWeight() + weigher()缓存对象大小差异大的场景
异步刷新refreshAfterWrite()高并发读取+后台更新
弱/软引用weakKeys()/softValues()内存敏感型应用

注意事项

1.刷新 vs 过期

  • 刷新 (refreshAfterWrite) 异步更新旧值,不阻塞请求
  • 过期 (expireAfterWrite) 会阻塞请求直到新值加载完成

2.权重计算

  • 确保 weigher 计算快速(纳秒级)
  • 权重总和不超过 maximumWeight

3.过期时间精度

.scheduler(Scheduler.systemScheduler())

默认时间精度≈1秒,需要毫秒级精度可配置:

4.并发加载控制

  • 相同key并发请求时,只有一个线程执行加载
  • 可通过 executor() 指定自定义线程池

到此这篇关于一文详解Caffeine缓存库的常用功能与使用指南的文章就介绍到这了,更多相关Caffeine缓存库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 了解Java多线程的可见性与有序性

    了解Java多线程的可见性与有序性

    这篇文章主要介绍了了解Java多线程的可见性与有序性,在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。,需要的朋友可以参考下
    2019-06-06
  • 详解Java的内存模型

    详解Java的内存模型

    本文更准确的说法应该是JVM的内存模型,但是这里又牵扯了一些其他的前置知识,主要是想从Java入手,从源头上梳理一遍整个Java底层运行的机制,中间会额外补充一些和题目无关的前置基础,导致主讲内存模型的篇幅所占的比例就不是那么绝对。
    2021-06-06
  • java判断对象中某个属性是否为空方法代码

    java判断对象中某个属性是否为空方法代码

    这篇文章主要给大家介绍了关于java判断对象中某个属性是否为空的相关资料,最近遇到后台接收值的时候,需要对接收对象进行非空校验,需要的朋友可以参考下
    2023-07-07
  • 基于spring boot实现一个全局异常处理器

    基于spring boot实现一个全局异常处理器

    在项目开发中,我们可以基于spring boot提供的切面特性,来很轻松的实现全局异常的处理,所以本文主要为大家介绍了如何基于spring boot实现一个全局异常处理器,有需要的可以参考下
    2023-09-09
  • JavaScript装饰器从基础到实战教程

    JavaScript装饰器从基础到实战教程

    装饰器是js中一种声明式语法特性,用于在不修改原始代码的情况下,动态扩展类、方法、属性或参数的行为,本文将从基础概念入手,逐步讲解装饰器的类型、用法、进阶技巧及实战场景,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 详解springboot整合ueditor踩过的坑

    详解springboot整合ueditor踩过的坑

    这篇文章主要介绍了详解springboot整合ueditor踩过的坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Java Lambda表达式的方法引用和构造器引用实例分析

    Java Lambda表达式的方法引用和构造器引用实例分析

    这篇文章主要介绍了Java Lambda表达式的方法引用和构造器引用,结合实例形式分析了Lambda表达式的方法引用和构造器引用相关原理、用法及操作注意事项,需要的朋友可以参考下
    2019-09-09
  • Mybatis-Plus 条件构造器 QueryWrapper 的基本用法

    Mybatis-Plus 条件构造器 QueryWrapper 的基本用法

    这篇文章主要介绍了Mybatis-Plus - 条件构造器 QueryWrapper 的使用,通过实例代码给大家介绍了查询示例代码及实现需求,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • 剑指Offer之Java算法习题精讲链表专题篇

    剑指Offer之Java算法习题精讲链表专题篇

    跟着思路走,之后从简单题入手,反复去看,做过之后可能会忘记,之后再做一次,记不住就反复做,反复寻求思路和规律,慢慢积累就会发现质的变化
    2022-03-03
  • Java Hashtable机制深入了解

    Java Hashtable机制深入了解

    HashTable是jdk 1.0中引入的产物,基本上现在很少使用了,但是会在面试中经常被问到。本文就来带大家一起深入了解一下Hashtable,需要的可以参考一下
    2022-09-09

最新评论