【缓存】JAVA本地缓存推荐Caffeine和Guava
🌟 引言
在软件开发过程中,缓存是提升系统性能的常用手段。对于基础场景,直接使用 Java集合框架(如Map/Set/List)即可满足需求。然而,当面对更复杂的缓存场景时:
- 需要支持多种过期策略(基于时间、访问频率等)
- 要求自动淘汰机制
- 需要线程安全等高级特性
自行实现这些功能往往复杂度较高。本文将介绍 Java 生态中成熟的两大主流本地缓存解决方案:Caffeine(新一代缓存之王)和Guava Cache(经典缓存方案)。
📊 核心维度对比
评估维度 | Caffeine | Guava Cache |
---|---|---|
性能 | ⚡ 读写吞吐量高5-10倍 | 🐢 中等性能 |
内存效率 | 🧠 更低内存占用(优化数据结构) | 📦 较高内存消耗 |
并发能力 | 🚀 无锁算法,百万级QPS | 🔒 分段锁,十万级QPS |
淘汰算法 | 🎯 TinyLFU + LRU 自适应 | ⏳ 标准LRU |
监控统计 | 📈 内置详细指标 | 📊 基础统计 |
JDK兼容性 | Java 8+ | Java 6+ |
社区活跃度 | 🌟 持续更新(2023年仍有新版本) | 🛑 维护模式(仅修复bug) |
🚀 Caffeine
Caffeine 是一个性能ISS(In-Space Sizing)的缓存框架,它使用无锁算法和分段锁机制,以更优的方式优化了缓存淘汰算法。Caffeine 的设计目标为极致性能,并针对一些常见的场景进行了优化。
🌟 特性
- 无锁算法和分段锁机制,以更优的方式优化了缓存淘汰算法。
- 高命中率,通过优化淘汰算法,Caffeine 显著提高缓存命中率。
- 更低内存开销,Caffeine 使用更小的内存结构,从而减少内存消耗。
- 线程安全,Caffeine 支持并发操作,保证线程安全。
🌟 如何使用
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.2.0</version>
</dependency>
import com.github.benmanes.caffeine.cache.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;public class CaffeineDemo {public static void main(String[] args) {basicUsageDemo();loadingCacheDemo();asyncLoadingCacheDemo();evictionDemo();statisticsDemo();}/*** 基础缓存操作示例*/public static void basicUsageDemo() {System.out.println("\n=== 1. 基础缓存操作 ===");Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 写入5秒后过期.maximumSize(100) // 最大100个条目.build();// 手动写入cache.put("key1", "value1");// 获取值(不存在返回null)String value = cache.getIfPresent("key1");System.out.println("获取key1: " + value); // 输出: value1// 获取或计算(线程安全)String value2 = cache.get("key2", k -> "computed-" + k);System.out.println("获取key2: " + value2); // 输出: computed-key2}/*** 自动加载缓存示例*/public static void loadingCacheDemo() {System.out.println("\n=== 2. 自动加载缓存 ===");LoadingCache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS) // 3秒未访问则过期.maximumSize(10).build(key -> {// 模拟从数据库加载System.out.println("正在加载: " + key);return "db-value-" + key;});// 自动触发加载函数System.out.println(cache.get("user1001")); // 输出: db-value-user1001System.out.println(cache.get("user1001")); // 第二次直接从缓存获取}/*** 异步加载缓存示例*/public static void asyncLoadingCacheDemo() {System.out.println("\n=== 3. 异步加载缓存 ===");AsyncLoadingCache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).maximumSize(1000).buildAsync(key -> {// 模拟异步加载return CompletableFuture.supplyAsync(() -> {System.out.println("异步加载: " + key);return "async-value-" + key;});});// 异步获取cache.get("id123").thenAccept(value -> {System.out.println("异步获取结果: " + value); // 输出: async-value-id123});}/*** 淘汰策略示例*/public static void evictionDemo() {System.out.println("\n=== 4. 淘汰策略 ===");Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3) // 测试用的小容量.removalListener((key, value, cause) -> System.out.printf("淘汰事件: key=%s, 原因=%s\n", key, cause)).build();cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");cache.put("k4", "v4"); // 触发淘汰(LRU)System.out.println("当前大小: " + cache.estimatedSize()); // 输出: 3}/*** 统计功能示例*/public static void statisticsDemo() {System.out.println("\n=== 5. 统计功能 ===");Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).recordStats() // 开启统计.build();cache.put("k1", "v1");cache.getIfPresent("k1");cache.getIfPresent("missingKey");CacheStats stats = cache.stats();System.out.println("命中率: " + stats.hitRate()); // 输出: 0.5System.out.println("命中数: " + stats.hitCount()); // 输出: 1System.out.println("未命中数: " + stats.missCount()); // 输出: 1}
}
🚀 Guava Cache
Guava Cache 是 Google 官方提供的一个缓存框架,它提供了许多高级特性,如自动加载、统计、序列化、并发控制等。与 Caffeine 不同,Guava Cache 的设计目标为简单易用,并支持更多的高级特性。
🌟 特性
- 自动加载、统计、序列化、并发控制等高级特性。
- 更高的并发控制,Guava Cache 使用更复杂的并发控制机制,以更优的方式解决并发问题。
🌟 如何使用
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.4.8-jre</version>
</dependency>
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.*;
import io.vavr.collection.List;
public class GuavaCacheDemo {public static void main(String[] args) throws ExecutionException {basicUsageDemo();loadingCacheDemo();cacheRemovalListenerDemo();cacheStatisticsDemo();advancedEvictionDemo();}/*** 基础缓存操作示例*/public static void basicUsageDemo() {System.out.println("\n=== 1. 基础缓存操作 ===");Cache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 写入5秒后过期.maximumSize(100) // 最大100个条目.concurrencyLevel(4) // 并发级别.build();// 手动写入cache.put("key1", "value1");// 获取值(不存在返回null)String value = cache.getIfPresent("key1");System.out.println("获取key1: " + value); // 输出: value1// 尝试获取不存在的keyString value2 = cache.getIfPresent("key2");System.out.println("获取不存在的key2: " + value2); // 输出: null}/*** 自动加载缓存示例*/public static void loadingCacheDemo() throws ExecutionException {System.out.println("\n=== 2. 自动加载缓存 ===");LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS) // 3秒未访问则过期.maximumSize(10).build(new CacheLoader<String, String>() {@Overridepublic String load(String key) {// 模拟从数据库加载System.out.println("正在加载: " + key);return "db-value-" + key;}});// 自动触发加载函数System.out.println(cache.get("user1001")); // 输出: db-value-user1001System.out.println(cache.get("user1001")); // 第二次直接从缓存获取// 批量获取System.out.println(cache.getAll(List.of("user1002", "user1003")));}/*** 缓存淘汰监听器示例*/public static void cacheRemovalListenerDemo() {System.out.println("\n=== 3. 淘汰监听器 ===");RemovalListener<String, String> listener = notification -> {System.out.printf("淘汰事件: key=%s, value=%s, 原因=%s\n", notification.getKey(), notification.getValue(),notification.getCause());};Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(3) // 测试用的小容量.removalListener(listener).build();cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");cache.put("k4", "v4"); // 触发淘汰(LRU)cache.invalidate("k2"); // 手动触发淘汰}/*** 缓存统计示例*/public static void cacheStatisticsDemo() {System.out.println("\n=== 4. 缓存统计 ===");Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(100).recordStats() // 开启统计.build();cache.put("k1", "v1");cache.getIfPresent("k1");cache.getIfPresent("missingKey");CacheStats stats = cache.stats();System.out.println("命中率: " + stats.hitRate()); // 输出: 0.5System.out.println("命中数: " + stats.hitCount()); // 输出: 1System.out.println("未命中数: " + stats.missCount()); // 输出: 1System.out.println("加载成功数: " + stats.loadSuccessCount());}/*** 高级淘汰策略示例*/public static void advancedEvictionDemo() {System.out.println("\n=== 5. 高级淘汰策略 ===");Cache<String, String> cache = CacheBuilder.newBuilder()// 基于权重的淘汰(假设不同value占用不同空间).maximumWeight(1000).weigher((String key, String value) -> value.length())// 弱引用key和value(适合缓存大对象).weakKeys().weakValues()// 定期维护(减少并发开销).concurrencyLevel(8).build();cache.put("long", "这是一个很长的字符串值");cache.put("short", "小");System.out.println("当前大小: " + cache.size());}
}
🎉 结论
对于大多数现代 Java 应用,Caffeine 无疑是更优选择,其卓越的性能表现和更低的内存开销使其成为新项目的首选。而 Guava Cache 则更适合已有 Guava 生态的遗留系统,或者需要特定功能(如 CacheLoader 深度集成)的场景。
终极建议: 新项目直接采用 Caffeine,老项目若无性能瓶颈可继续使用 Guava Cache,在遇到性能问题时再考虑迁移。两者 API 相似,迁移成本较低。
相关文章:
【缓存】JAVA本地缓存推荐Caffeine和Guava
🌟 引言 在软件开发过程中,缓存是提升系统性能的常用手段。对于基础场景,直接使用 Java集合框架(如Map/Set/List)即可满足需求。然而,当面对更复杂的缓存场景时: 需要支持多种过期策略&#x…...
Prometheus的服务命令和配置文件
一、Prometheus的服务端命令和启动方式 1.服务端命令(具体详情可以--help查看) --config.file“prometheus.yml”指定配置文件,默认是当前目录下的prometheus.yml--web.listen-address"0.0.0.0:9090"web页面的地址与端口…...

物流项目第五期(运费计算实现、责任链设计模式运用)
前四期: 物流项目第一期(登录业务)-CSDN博客 物流项目第二期(用户端登录与双token三验证)-CSDN博客 物流项目第三期(统一网关、工厂模式运用)-CSDN博客 物流项目第四期(运费模板列…...
前端JavaScript-嵌套事件
点击 如果在多层嵌套中,对每层都设置事件监视器,试试看 <!DOCTYPE html> <html lang"cn"> <body><div id"container"><button>点我!</button></div><pre id"output…...

X 下载器 2.1.42 | 国外媒体下载工具 网页视频嗅探下载
X 下载器让你能够轻松地从社交应用如Facebook、Instagram、TikTok等下载视频和图片。通过内置浏览器访问网站,它能自动检测视频和图片,只需点击下载按钮即可完成下载。去除广告,解锁本地会员,享受无广告打扰的下载体验。 大小&am…...

STM32 CAN CANAerospace
STM32的CAN模块对接CANAerospace 刚开始报错如下. 设备开机后整个CAN消息就不发了. USB_CAN调试器报错如下. index time Name ID Type Format Len Data00000001 000.000.000 Event 总线错误 DATA STANDARD 8 接收过程错误-格…...

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取
1 简介 为了有效地利用雾状冰生长的物理现象,最近开发了一种优化算法——雾状优化算法(RIME)。它模拟硬雾状和软雾状过程,构建硬雾状穿刺和软雾状搜索机制。在本研究中,引入了一种增强版本,称为修改的RIME…...

服务器安装xfce桌面环境并通过浏览器操控
最近需要运行某个浏览器的脚本,但是服务器没有桌面环境,无法使用,遂找到了KasmVNC,并配合xfce实现低占用的桌面环境,可以直接使用浏览器进行操作 本文基于雨云——新一代云服务提供商的Debian11服务器操作,…...
Java设计模式之组合模式:从入门到精通(保姆级教程)
文章目录 1. 组合模式概述1.1 专业定义1.2 通俗解释1.3 模式结构2. 组合模式详细解析2.1 模式优缺点2.2 适用场景3. 组合模式实现详解3.1 基础实现3.2 代码解析4. 组合模式进阶应用4.1 透明式 vs 安全式组合模式4.2 组合模式与递归4.3 组合模式与迭代器5. 组合模式在实际开发中…...

Oracle 创建外部表
找别人要一下数据,但是他发来一个 xxx.csv 文件,怎么办? 1、使用视图化工具导入 使用导入工具导入,如 DBeaver,右击要导入的表,选择导入数据。 选择对应的 csv 文件,下一步就行了(如…...

大语言模型 17 - MCP Model Context Protocol 介绍对比分析 基本环境配置
MCP 基本介绍 官方地址: https://modelcontextprotocol.io/introduction “MCP 是一种开放协议,旨在标准化应用程序向大型语言模型(LLM)提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…...

【软考向】Chapter 9 数据库技术基础
基本概念数据库的三级模式结构 数据模型E-R 模型关系模型各种键完整性约束 关系代数5 种基本的关系代数运算:并、差、笛卡儿积、投影和选择扩展的关系代数运算:交(Intersection)、连接(Join)、除(Division)、广义投影(Generalized Projection)、外连接(O…...

实战:Dify智能体+Java=自动化运营工具!
我们在运营某个圈子的时候,可能每天都要将这个圈子的“热门新闻”发送到朋友圈或聊天群里,但依靠传统的实现手段非常耗时耗力,我们通常要先收集热门新闻,再组装要新闻内容,再根据内容设计海报等。 那怎么才能简化并高…...

STM32单片机GUI系统1 GUI基本内容
目录 一、GUI简介 1、emWin 2、LVGL (Light and Versatile Graphics Library) 3、TouchGFX 4、Qt for Embedded 5、特性对比总结 二、LVGL移植要求 三、优化LVGL运行效果方法 四、LVGL系统文件 一、GUI简介 在嵌入式系统中,emWin、LVGL、TouchGFX 和 Qt 是…...
从零开始学习three.js(21):一文详解three.js中的矩阵Matrix和向量Vector
一、三维世界的数学基石 在Three.js的三维世界里,所有视觉效果的实现都建立在严密的数学基础之上。其中向量(Vector) 和矩阵(Matrix) 是最核心的数学工具,它们就像构建数字宇宙的原子与分子,支…...

应届本科生简历制作指南
一、找一个专业的简历模板 首先,你需要访问 Overleaf 的官方网站,也就是Overleaf, Online LaTeX Editor,进入页面后,点击注册按钮,按照提示填写相关信息来创建一个属于自己的账号,通常需要填写用户名、邮箱…...
VUE3+TS实现图片缩放移动弹窗
完整代码 使用VUE3、TS,实现将图片通过鼠标拖拽缩放以及选择缩放比例。 <template><div><el-dialogv-model"dialogVisible"title"查看图片":close-on-click-modal"false":close-on-press-escape"false"fu…...
大语言模型训练数据格式:Alpaca 和 ShareGPT
在大规模语言模型(LLM)的开发中,训练数据的质量和格式起着至关重要的作用。为了更好地理解和构建高质量的数据集,社区发展出了多种标准化的数据格式。其中,Alpaca 和 ShareGPT 是两种广泛使用的训练数据格式࿰…...
实现动态增QuartzJob,通过自定义注解调用相应方法
:::tip 动态增加Quartz定时任务,通过自定义注解来实现具体的定时任务方法调用。 ::: 相关依赖如下 <!-- 用来动态创建 Quartz 定时任务 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-start…...

PyTorch可视化工具——使用Visdom进行深度学习可视化
文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…...
Qt无边框界面添加鼠标事件
在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例: 1. 创建无边框窗口 首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志: #include <QWidget> #in…...

企业级爬虫进阶开发指南
企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …...
Ubuntu ping网络没有问题,但是浏览器无法访问到网络
我这边是尝试清楚DNS缓存然后重新访问就可以了。 使用 resolvectl 刷新 DNS 缓存 在 Ubuntu 20.04 及更高版本中,可以使用以下命令来刷新 DNS 缓存: sudo resolvectl flush-caches 使用 systemd-resolve(适用于旧版本) 如果你…...

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】
################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…...

数据结构实验10.1:内部排序的基本运算
文章目录 一,实验目的二,实验内容1. 数据生成与初始化2. 排序算法实现(1)直接插入排序(2)二分插入排序(3)希尔排序(4)冒泡排序(5)快速…...
C#:多线程
一.线程常用概念 线程(Thread):操作系统执行程序的最小单位 进程(Process):程序在内存中的运行实例 并发(Concurrency):多个任务交替执行(单核CPU࿰…...
基于Zynq SDK的LWIP UDP组播开发实战指南
一、为什么选择LWIP组播? 在工业控制、智能安防、物联网等领域,一对多的高效数据传输需求日益增长。Zynq-7000系列SoC凭借其ARM+FPGA的独特架构,结合LWIP轻量级网络协议栈,成为嵌入式网络开发的理想选择。本文将带您实现: LWIP组播配置全流程动态组播组切换技术零拷贝数据…...
c#将json字符串转换为对象数组
在C#中,将JSON字符串转换为对象数组是一个常见的需求,特别是在处理来自Web API的响应或需要反序列化本地文件内容时。这可以通过使用Newtonsoft.Json(也称为Json.NET)库或.NET Core内置的System.Text.Json来完成。以下是如何使用这…...
机器学习在智能水泥基复合材料中的应用与实践
“机器学习在智能水泥基复合材料中的应用与实践” 课程 内容 机器学习基础模型与复合材料研究融合 机器学习在复合材料中的应用概述机器学习用于复合材料研究的流程复合材料数据收集与数据预处理 实例:数据的收集和预处理 复合材料机器学习特征工程与选择 实例&a…...

wps编辑技巧
1、编辑模式 2、图片提取方法:右键保存图片 可以直接右键保存下来看看是否是原始图,如果歪着的图,可能保存下来是正的,直接保存试下 3、加批注...