实用指南:Java-187 Guava Cache 并发参数与 refreshAfterWrite 实战:LoadingCache 动态加载与自定义 LRU 全解析

news/2026/1/16 19:07:45/文章来源:https://www.cnblogs.com/yangykaifa/p/19493712

TL;DR

  • 场景:在高并发 Java 服务中使用 Guava Cache 做本地缓存,同时需要控制刷新时延与内存占用。
  • 结论:合理设置 concurrencyLevel + refreshAfterWrite,并理解 LoadingCache 的单 key 加锁语义,可在保证线程安全的前提下拿到接近 ConcurrentHashMap 的并发性能。
  • 产出:给出并发参数选型思路、refresh 阻塞行为理解框架,以及对比自定义 LinkedHashMap LRU 的实现边界,方便在项目中直接落地配置。

Java-187 Guava Cache 并发参数与 refreshAfterWrite 实战:LoadingCache 动态加载与自定义 LRU 全解析

版本矩阵

组件 / 能力版本 / 范围已验证说明
JDK 81.8.x典型存量项目环境,Guava Cache 并发与 refreshAfterWrite 行为一致
JDK 1111.x服务器端主流 LTS,示例代码与语义无差异
JDK 1717.x新项目常用 LTS,适用于文中所有配置与示例
Guava Cache 核心 API23.x–32.x+CacheBuilder、LoadingCache、concurrencyLevel、refreshAfterWrite 语义稳定
并发分段实现(Segment/Striped)与 JDK ConcurrentHashMap 思路对齐通过 segmentFor(hash) 进行分段定位,减少锁竞争
自定义 LRU(LinkedHashMap)JDK 标准库基于 removeEldestEntry 的访问顺序 LRU,适合单线程或外层自行加锁场景

Guava Cache

并发设置

Guava Cache 通过设置 concurrencyLevel 参数来优化并发性能,使得缓存能够高效地支持多线程环境下的并发读写操作。以下是关于该机制的详细说明:

  1. 并发级别参数详解:

  2. 底层实现原理:

  3. 性能优化建议:

  4. 实际应用示例:

Cache<String, Object> cache = CacheBuilder.newBuilder().concurrencyLevel(8)  // 设置为8个分段.maximumSize(1000).build();
  1. 注意事项:
    • 该参数只在缓存构建时生效,创建后不可修改
    • 与maximumSize配合使用时,每个Segment会平均分配容量限制
    • 在极高并发场景下,可考虑结合refreshAfterWrite使用

这种设计使得Guava Cache在保持线程安全的同时,能够获得接近并发哈希表的性能表现,特别适合作为高性能应用中的本地缓存解决方案。

LoadingCache<String, Object> cache = CacheBuilder.newBuilder().maximumSize(3)// 根据CPU情况进行并发.concurrencyLevel(Runtime.getRuntime().availableProcessors()).build(new CacheLoader<String, Object>() {@Overridepublic String load(String key) throws Exception {return "get: " + key;}});

concurrencyLevel = Segment 数组的长度,同 ConcurrentHashMap 类似 Guava Cache 的并发也是通过分离锁实现的:

@CanIgnoreReturnValue // TODO(b/27479612): consider removing this
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {int hash = hash(checkNotNull(key));return segmentFor(hash).get(key, hash, loader);}

LoadingCache 采用了类似 ConcurrentHashMap 的方式,将映射表分多个 Segment,Segment 之间可以并发访问,这样可以大大的提高并发效率,使得并发冲突的可能性降低了。

更新锁定

Guava Cache 提供了一个 refreshAfterWrite 定时刷新数据的配置项,这个特性主要用于保证缓存数据的时效性。当配置了 refreshAfterWrite 后,如果缓存项在指定时间内没有被更新或覆盖,则会在下一次获取该值的时候触发刷新机制。

刷新过程的具体实现如下:

  1. 后台会启动一个异步线程去回源(如数据库、远程接口等)获取最新数据
  2. 在刷新期间,所有对该缓存项的请求会被阻塞(block),默认阻塞时间为 1 分钟
  3. 刷新过程中只有一个请求会实际执行回源操作,避免了并发回源导致的系统压力
  4. 如果在阻塞时间内成功获取到新值,则返回新值并更新缓存
  5. 如果超过阻塞时间仍未获取到新值,则会返回旧值,保证系统不会因为刷新失败而不可用

典型应用场景包括:

  • 配置信息缓存(如系统参数、开关配置等)
  • 商品信息缓存(如价格、库存等)
  • 热点数据缓存(如排行榜数据)

示例配置代码:

CacheBuilder.newBuilder()
.refreshAfterWrite(5, TimeUnit.MINUTES)  // 5分钟未更新则触发刷新
.build(new CacheLoader<String, Object>() {@Overridepublic Object load(String key) throws Exception {return fetchDataFromDB(key);  // 数据加载逻辑}});

注意事项:

  1. 与 expireAfterWrite 不同,refreshAfterWrite 不会自动移除过期数据
  2. 建议设置合理的 refresh 时间,避免过于频繁的回源操作
  3. 阻塞时间可以通过重载 CacheLoader 的 reload 方法来自定义
LoadingCache<String, Object> cache = CacheBuilder.newBuilder().maximumSize(3)// 根据 CPU 数量并发.concurrencyLevel(Runtime.getRuntime().availableProcessors())// 3秒内阻塞的话会返回旧的数据.refreshAfterWrite(3, TimeUnit.SECONDS).build(new CacheLoader<String, Object>() {@Overridepublic String load(String key) throws Exception {return "get: " + key;}});

动态加载

动态加载行为是缓存系统中常见的机制,它通常发生在以下两种场景:

  1. 首次获取数据时缓存中不存在该数据
  2. 缓存中的数据已经过期(基于时间或大小的过期策略)

Guava Cache 采用了一种优雅的回调模式来实现动态加载。具体实现机制如下:

  1. 回调模式设计

    • 用户需要预先定义数据加载方式(Loader)
    • 当缓存需要加载新数据时,会自动回调这个预定义的加载方式
    • 这种设计遵循了"好莱坞原则"(不要调用我们,我们会调用你)
  2. 线程安全处理

    • 当多个线程同时请求同一个缺失的key时
    • Guava Cache 会确保只有一个线程执行加载操作
    • 其他线程会等待加载完成并共享结果
  3. 代码实现示例

// 获取对应哈希段的Segment对象
Segment<K, V> segment = segmentFor(hash);// 调用get方法,传入key、hash值和LoaderV value = segment.get(key, hash, loader);

其中关键组件说明:

  • loader:用户自定义的数据加载逻辑,需要实现CacheLoader接口
  • segmentFor(hash):Guava Cache的分段锁实现,用于提高并发性能
  • get()方法内部会先检查缓存,若不存在则调用loader加载数据

典型应用场景:

  1. 数据库查询缓存
  2. 耗时计算结果的缓存
  3. 远程服务调用结果的缓存

这种设计既保证了线程安全,又提供了良好的扩展性,让使用者可以专注于业务逻辑的实现。

自定义LRU

package icu.wzk;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapCache<K, V> {private int limit;private LRUCache<K, V> internalCache;public LinkedHashMapCache(int limit) {this.limit = limit;this.internalCache = new LRUCache<>(limit);}public V get(K key) {return internalCache.get(key);}public void put(K key, V value) {this.internalCache.put(key, value);}public static void main(String[] args) {LinkedHashMapCache<String, String> cache = new LinkedHashMapCache<>(3);// 放入三个数据cache.put("1", "1");cache.put("2", "2");cache.put("3", "3");// 第四个数据cache.put("4", "4");for (Object o : cache.internalCache.values()) {System.out.println(o);}}}class LRUCache<K,V> extends LinkedHashMap<K, V> {private final int limit;public LRUCache(int limit) {super(limit, 0.75f, true);this.limit = limit;}@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {// 移除老的数据return size() > limit;}}

我们尝试运行,结果如下所示:
自定义实现LRU Cache

错误速查

症状根因定位修复
并发线程数上来后,缓存命中变差、Full GC 频繁concurrencyLevel 设置过大,Segment 数量过多导致元数据与锁开销放大检查 CacheBuilder.newBuilder() 中并发参数与实际 QPS、线程数将 concurrencyLevel 控制在预估并发线程数附近,避免盲目按 CPU×N 放大
以为配置了 refreshAfterWrite 后,过期数据会自动清除混淆 refreshAfterWrite(刷新)与 expireAfterWrite(过期剔除)通过日志或监控看到 key 长时间存在但有后台回源请求需要同时根据业务配置 expireAfterWrite 或主动 invalidate
高峰期 read 被卡住,线程堆栈停在 CacheLoader.load 附近load / 回源逻辑过慢,且 refreshAfterWrite 单 key 刷新会阻塞其他请求打线程 dump,观察大量线程阻塞在同一 key 的加载路径优化回源逻辑、增加超时与降级,必要时拆 key 或引入多级缓存
刷新期间期待“后台异步不阻塞”,实际请求却被挂起误解 refresh 语义:单 key 刷新期间,其他请求默认等待结果结合文档与代码调试发现 refresh 期间返回时间抖动根据业务接受度决定:改用 expire+懒加载,或对热点 key 单独设计缓存
多线程场景下自定义 LinkedHashMapCache 偶现数据错乱或 NPELinkedHashMap 非线程安全,外层未加锁就直接在并发环境下复用在压测或线上日志中发现 size 与实际访问不一致、偶发异常若需要并发,外层用 Collections.synchronizedMap 或 ReentrantLock 包裹,或直接改用 Guava Cache
maximumSize=3 等非常小,QPS 略高即频繁触发淘汰,命中率极差LRU 容量过小,未结合实际 key 数和访问分布评估 cache size监控中 eviction 数量远高于命中数根据热 key 数量与访问模式重新估算 maximumSize,适当上调缓存容量
使用 Runtime.getRuntime().availableProcessors() 直接套到并发CPU 核心数≠真实并发访问线程数,导致过高或过低的锁分段配置对比线程池大小、Tomcat 连接数与 CPU 核心数,发现不匹配以“高并发线程数”为主维度评估 concurrencyLevel,而不是机械等于 CPU 数

其他系列

AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究,持续打造实用AI工具指南!
AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地
AI模块直达链接

Java篇持续更新中(长期更新)

Java-180 Java 接入 FastDFS:自编译客户端与 Maven/Spring Boot 实战
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS正在更新… 深入浅出助你打牢基础!
Java模块直达链接

大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
大数据模块直达链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1169567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析:5G工业路由器的深层架构:从射频热管理到链路状态机

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

程序员必看!LLM读不懂工业标准?试试这个Ontology-aware KG-RAG框架,表格任务直接起飞!

一、LLM 单啃工业标准会“消化不良”&#xff1f; 船舶、海工、能源等行业的工业标准&#xff08;ASTM、API、ISO 等&#xff09;往往长这样&#xff1a; 一份文档 60 页&#xff0c;层层嵌套“1-1.1-1.1.1-Table 3-Note b”&#xff1b;一段句子包含条件-例外-数值-单位四连…

淋雨试验箱优质供应商排行榜:帮你避开选购陷阱,找到真正靠谱的厂家 - 品牌推荐大师1

在“中国制造2025”战略和产业升级的双重推动下,中国环境试验设备行业迎来了黄金发展期。淋雨试验箱作为评估产品外壳防水性能的关键设备,其市场需求随着新能源汽车、5G通信、消费电子等行业的蓬勃发展而持续增长。 …

救命!我的AI只会说“好的“!揭秘大模型“规划模式“,让AI自己干活才是真AI!

让智能体“组织一次10人的团队团建”&#xff0c;它直接回复“好的”&#xff0c;却迟迟不给出具体方案&#xff1b;让它“处理新员工入职流程”&#xff0c;它只想到了创建账号&#xff0c;却遗漏了培训分配、部门对接等关键步骤&#xff1b;让它“生成一份季度业务分析报告”…

从 AIGC 检测逻辑看论文降重工具的实际差异 - 品牌观察员小捷

起因:为什么“降重完成”≠“审核安全” 在最近一年的论文修改实践中,越来越多用户发现:论文重复率已合格 但 AIGC 检测仍提示高风险 这并非偶然,而是因为 AIGC 检测关注的不是“抄没抄”,而是“像不像人写的”。…

【硬核推荐】Lemon AI Agent:不会编程也能开发神器?从需求到交付全自动搞定,小白秒变大神!

Lemon 是一款开源的General AI Agent通用智能体&#xff0c;能够从需求计划到成果交付全流程自动化&#xff0c;它通过独立思考和系统规划&#xff0c;在虚拟环境中灵活调用各类工具&#xff0c;如编写并执行代码、智能浏览网页、操作网页应用、命令执行等。Lemon 擅长将复杂任…

深度测评:主管护师内科护理学考试看什么书能实现事半功倍 - 医考机构品牌测评专家

深度测评:主管护师内科护理学考试看什么书能实现事半功倍随着医疗行业对护理专业要求的持续提升,主管护师内科护理学考试作为护理职称晋升体系中的重要组成部分。内科护理学涵盖呼吸、循环、消化、泌尿、内分泌等多个…

深度测评:主管护师内科护理学考试看什么书能实现事半功倍 - 医考机构品牌测评专家

深度测评:主管护师内科护理学考试看什么书能实现事半功倍随着医疗行业对护理专业要求的持续提升,主管护师内科护理学考试作为护理职称晋升体系中的重要组成部分。内科护理学涵盖呼吸、循环、消化、泌尿、内分泌等多个…

亲测好用!8款AI论文平台测评:本科生毕业论文全攻略

亲测好用&#xff01;8款AI论文平台测评&#xff1a;本科生毕业论文全攻略 2026年AI论文平台测评&#xff1a;为何值得一看 随着人工智能技术的不断进步&#xff0c;AI论文平台逐渐成为本科生撰写毕业论文的重要辅助工具。然而&#xff0c;面对市场上琳琅满目的选择&#xff0c…

外科护理(370)主管护师备考听什么课?精选课程全测评 - 医考机构品牌测评专家

外科护理(370)主管护师备考听什么课?精选课程全测评随着医疗行业对护理专业化要求的不断提升,外科护理主管护师作为外科护理领域的核心骨干,其职称认证的重要性日益凸显。近年来,外科护理主管护师考试呈现出新的…

探索三菱PLC方案之FX2N源码V9.x高性能版

三菱 plc方案源码STM32工控板fx2n源码 FX2N源码V9.x完善高性能版&#xff0c;程序架构清晰&#xff0c;注释详细&#xff0c;支持大部分指令&#xff0c;当前最新功能如下&#xff1a; 13、FX2N源码持续升级中。 。 。 亲们&#xff0c;敬请关注&#xff01; 12、新增3条指令&a…

外科护理(370)主管护师备考听什么课?筑基提能的科学进阶指南 - 医考机构品牌测评专家

外科护理(370)主管护师备考听什么课?筑基提能的科学进阶指南在医疗资源持续紧张、医务工作者任务繁重的当下,我国医疗人才队伍建设正面临前所未有的挑战。随着健康中国战略的深入推进,对高素质护理人才的需求日益…

外科护理(370)主管护师备考听什么课?筑基提能的科学进阶指南 - 医考机构品牌测评专家

外科护理(370)主管护师备考听什么课?筑基提能的科学进阶指南在医疗资源持续紧张、医务工作者任务繁重的当下,我国医疗人才队伍建设正面临前所未有的挑战。随着健康中国战略的深入推进,对高素质护理人才的需求日益…

主管护师内科护理学考试看什么书?精华资料分享 - 医考机构品牌测评专家

主管护师内科护理学考试看什么书?精华资料分享各位护理同仁,大家好!我是一名在医院内科工作多年的护师,去年刚刚通过了内科护理学考试。回想起备考的那段日子,最让我头疼的不是知识本身,而是面对市面上琳琅满目的…

主管护师内科护理学考试看什么书?精华资料分享 - 医考机构品牌测评专家

主管护师内科护理学考试看什么书?精华资料分享各位护理同仁,大家好!我是一名在医院内科工作多年的护师,去年刚刚通过了内科护理学考试。回想起备考的那段日子,最让我头疼的不是知识本身,而是面对市面上琳琅满目的…

【AI干货】多路由器+LLM重排序!RAG冠军方案开源,大模型开发者必学技术栈

公司年报智能问答比赛任务简介 比赛的任务是基于公司年度报告构建一个问答系统。简单来说&#xff0c;比赛当天的流程如下&#xff1a; 我们会收到来自随机挑选公司的 100 份年度报告&#xff0c;并需要在 2.5 小时内解析这些报告并构建一个数据库。这些报告是 PDF 格式&…

【AI编程新风口】保姆级LangGraph教程:19期精华总结,小白也能手搓AI Agent,告别被替代焦虑!

重要的不是我们是否会被AI替代&#xff0c; 而是我们要比被替代的人更懂AI。 大家好&#xff0c;自去年9月开始计划认真学习LangGraph以来&#xff0c;我在主业之余尽力保持一周一更的节奏&#xff0c;没想到也坚持了19期了。回顾截至目前的学习历程&#xff0c;我们从最基本…

2026年GEO项目源码哪家靠谱?源头作者/厂商汇总 - 源码云科技

2026年GEO项目源码哪家靠谱?源头作者/厂商汇总随着AI搜索流量占比突破60%,GEO优化已成为企业数字营销的必争之地,而选对靠谱的GEO优化源码和源头厂商,直接决定了AI获客的效果与利润空间。2026年市面上GEO优化服务商…

2026年GEO工具源码源头推荐,高效实用款盘点 - 源码云科技

2026年GEO工具源码源头推荐,高效实用款盘点随着AI搜索用户规模突破5.15亿,信息获取方式从“点击浏览”转向“对话生成”,GEO优化已成为企业抢占流量红利的核心赛道。2026年GEO市场规模预计飙升至30亿元,同比增长超…

AIGC 论文检测与降重:不同工具在真实场景下的差异 - 品牌观察员小捷

一、背景:为什么传统查重已不足以覆盖风险 在多数高校与期刊系统中,论文审核已逐步拆分为两条并行路径:文本重复率检测(Similarity) AIGC 生成痕迹检测(Generative Pattern)前者关注“是否抄袭已有文本”,后者…