【缓存】本地缓存Caffeine AsyncCache实现原理与最佳实践
AsyncCacheJVM 级 SingleFlight 实现原理与最佳实践本文基于CaffeineAsyncCacheJava 8适用于需要高并发下防止缓存击穿、避免重复加载的场景。一、核心目标同一 Key 只加载一次在高并发场景下缓存失效瞬间可能出现缓存击穿Cache Breakdown大量线程同时发现缓存缺失同时去 DB / RPC 加载同一份数据。AsyncCache的目标就是无论多少线程并发访问同一个 Key 只触发一次加载逻辑。二、核心实现原理1. 原子性保障ConcurrentHashMap.computeIfAbsentAsyncCache底层依赖ConcurrentHashMap核心逻辑等价于CompletableFutureVfuturemap.computeIfAbsent(key,k-{// ✅ 同一时刻只有一个线程能进入此处CompletableFutureVfnewCompletableFuture();executor.execute(()-{try{VvalueloadFromDb(k);f.complete(value);}catch(Throwablet){f.completeExceptionally(t);map.remove(k,f);// 加载失败允许重试}});returnf;});关键保证特性说明原子性computeIfAbsent对同一 key 所在桶加锁保证创建 Future 是原子操作可见性Node.value和next用volatile修饰Happens-Before 规则保证对其他线程立即可见唯一性同一 key 永远只创建一个CompletableFuture等待机制未抢到锁的线程直接拿到已有 Future自然等待结果✅这就是 JVM 级的 SingleFlight 实现2. Java 8 的并发控制CAS synchronized桶级锁⚠️重要更正Java 8 中ConcurrentHashMap已废弃 JDK 7 的 Segment 分段锁Striped Locking改用CAS 无锁 synchronized桶级锁的混合策略。JDK 7 vs JDK 8 对比维度JDK 7已淘汰JDK 8当前主流数据结构Segment[]HashEntry[] 链表Node[] 链表 / 红黑树锁机制ReentrantLock分段锁Striped LockingCAS synchronized桶级锁锁粒度Segment 级别默认 16 段单个桶的头节点并发度 数组长度读操作volatile保证可见性volatile保证可见性完全无锁写操作先获取 Segment 锁先 CAS 尝试失败再synchronized锁桶扩容单个 Segment 独立扩容多线程协同扩容ForwardingNode标记哈希冲突退化纯链表 O(n)链表 ≥ 8 且容量 ≥ 64 时转红黑树 O(log n)computeIfAbsent的执行流程Java 8线程 T1、T2、T3 同时调用 computeIfAbsent(sameKey, ...) │ ▼ ① 计算 hash定位到桶下标 i │ ▼ ② 桶为空── 是 ──→ CAS 直接插入无锁快路径 │ 否 ▼ ③ synchronized 锁住桶的头节点 │ ▼ ④ 再次检查 key 是否仍不存在double check │ ▼ ⑤ 只有一个线程执行 mappingFunction │ ▼ ⑥ 释放锁其他线程拿到同一个 Future核心要点CAS 优先无竞争时完全无锁性能极高synchronized锁桶头节点只有发生哈希冲突时才加锁且锁粒度极小锁升级机制JVM 会自动将synchronized从偏向锁 → 轻量级锁 → 重量级锁逐步升级绝大多数场景停留在轻量级锁阶段不同桶之间完全无竞争并发度约等于桶数组长度默认 16可随扩容增长为什么 Java 8 选择synchronized而非ReentrantLock对比项ReentrantLockJDK 7synchronizedJDK 8锁粒度Segment 级较粗桶头节点级更细JVM 优化无特殊优化偏向锁、轻量级锁、自旋、锁消除、锁粗化内存开销每个 Segment 一个锁对象锁信息内嵌在对象头中零额外对象可中断支持lockInterruptibly()不支持但缓存场景不需要公平性可配置不可配置但 FIFO 等待已足够实际性能较好更优尤其高并发 短临界区结论在ConcurrentHashMap这种锁持有时间极短、不需要条件变量和中断特性的场景下synchronized经过 JVM 优化后性能全面优于ReentrantLock且零内存开销。3. 自动清理与内存安全Future 完成后引用由 GC 自动回收加载失败时主动remove(key)避免空值缓存永久阻塞内存泄漏三、推荐写法✅ 基础用法最推荐publicStringgetData(Stringkey){returnasyncCache.get(key,(k,exec)-CompletableFuture.supplyAsync(()-loadDataFromDb(k),exec)).join();}execCaffeine 内置ForkJoinPool生产环境建议自定义线程池join()阻塞等待结果适合非响应式服务✅ 带超时保护防止线程堆积publicStringgetDataWithTimeout(Stringkey){returnasyncCache.get(key,(k,exec)-CompletableFuture.supplyAsync(()-loadDataFromDb(k),exec).completeOnTimeout(fallback,2,TimeUnit.SECONDS)).join();}✅ 防止DB 慢查询RPC 无限阻塞线程池被打爆✅ 防止缓存污染加载失败不缓存publicStringgetDataSafe(Stringkey){returnasyncCache.get(key,(k,exec)-CompletableFuture.supplyAsync(()-loadDataFromDb(k),exec).exceptionally(ex-{asyncCache.synchronous().invalidate(k);thrownewRuntimeException(ex);})).join();}非常重要否则失败结果会被缓存导致后续请求全部失败。五、原子更新 Value如果你是想做CAS 风格更新cache.asMap().compute(key,(k,oldFuture)-{if(oldFuturenull){returnCompletableFuture.completedFuture(init);}returnoldFuture.thenApply(v-v_updated);});⚠️ 注意AsyncCache中 Value 是CompletableFuture更新成本较高通常不建议频繁使用六、并发测试验证publicstaticvoidmain(String[]args){AsyncCacheString,StringcacheCaffeine.newBuilder().buildAsync();AtomicIntegerloadCountnewAtomicInteger(0);ListCompletableFutureStringfuturesIntStream.range(0,10).parallel().mapToObj(i-cache.get(sameKey,(key,exec)-{System.out.println(Thread.currentThread().getName() loading...);loadCount.incrementAndGet();returnCompletableFuture.supplyAsync(()-Data-key);})).toList();futures.get(0).whenComplete((v,ex)-{System.out.println(Result: v);});System.out.println(Load count loadCount.get());// ✅ 永远是 1}典型输出ForkJoinPool.commonPool-worker-1 loading... Result: Data-sameKey Load count 1✅完美证明仅一个线程执行加载逻辑七、与 Redis 分布式锁对比维度AsyncCacheJVM 级Redis Lock分布式作用范围单 JVM 内跨 JVM / 跨机器锁机制CAS synchronized桶级锁SETNX / Redlock性能⭐⭐⭐⭐⭐纳秒~微秒级⭐⭐毫秒级 网络 IO复杂度低开箱即用高需处理超时、死锁、脑裂网络 IO无有每次加锁至少 1 次 RTT适用场景单机本地缓存防击穿分布式协调、跨服务互斥✅结论能使用AsyncCache解决的场景不要用 Redis 锁。二者不是替代关系而是互补——分布式场景仍需 Redis单机高并发场景AsyncCache是更优解。八、避坑指南❌ 坑 1在computeIfAbsent的 Lambda 中再次操作同一个 Map// 错误示范可能导致死锁map.computeIfAbsent(key,k-{map.put(otherKey,someValue);// ⚠️ Lambda 内可能持有桶锁再次操作可能死锁returncomputeValue();});✅正确做法Lambda 内只做纯计算不涉及任何 Map 写操作。❌ 坑 2mappingFunction返回nullcomputeIfAbsent的mappingFunction不允许返回 null否则抛出NullPointerException。// 错误cache.get(key,k-null);// NPE!// 正确返回包装类型或 Optionalcache.get(key,k-CompletableFuture.completedFuture(null));// OK[citation:6]❌ 坑 3mappingFunction执行时间过长computeIfAbsent在执行 Lambda 时持有桶锁虽然时间极短如果 Lambda 内做耗时操作会阻塞同一桶的其他操作。✅正确做法Lambda 内只创建CompletableFuture实际计算交给异步线程// ✅ 推荐Lambda 立即返回 Future计算异步执行cache.get(key,(k,exec)-{returnCompletableFuture.supplyAsync(()-loadDataFromDb(k),exec);});九、总结CaffeineAsyncCache通过ConcurrentHashMap.computeIfAbsent实现了 JVM 级的 SingleFlight。在 Java 8 中底层依赖 CAS 无锁 synchronized桶级锁的混合策略以极低的锁开销保证同一 Key 只加载一次。核心要点速查要点一句话说明原子性来源ConcurrentHashMap.computeIfAbsentJava 8CAS synchronized桶级锁锁粒度单个桶的头节点不同桶之间零竞争无锁路径桶为空时 CAS 直接插入完全无锁等待机制所有线程共享同一个CompletableFuture失败处理主动remove允许重试超时保护completeOnTimeout防止永久阻塞分布式场景仍需 Redis / DB 层协调最大陷阱Lambda 内不要操作同一个 Map不要返回 null十、参考与延伸阅读JDK 源码ConcurrentHashMap.computeIfAbsent()JDK 8u60Caffeine 官方文档https://github.com/ben-manes/caffeineJava 8synchronized锁升级机制偏向锁 → 轻量级锁 → 重量级锁CompletableFuture超时 APIJava 9completeOnTimeout/ Java 8 GuavaFutures.withTimeout

相关新闻

zoho desk属于什么档次

zoho desk属于什么档次

Zoho Desk 属于中高端、偏实用型的智能工单系统,更适合需要多渠道接入、工单自动化、AI 辅助、知识库、报表分析和本地化服务支持的企业。它不是只面向简单在线咨询的小工具,也不是必须依赖大型定制项目才能落地的重型平台,而是介于“轻量客服…

2026/8/6 1:35:01 阅读更多 →
GBase数据库AI融合方案亮相中国工业软件大会(下)

GBase数据库AI融合方案亮相中国工业软件大会(下)

2026年7月31日,第六届中国工业软件大会在重庆召开。在大会“人工智能工业软件产业融合发展论坛”上,南大通用(gbase database)董事长丁明峰发表 《夯实工业智能底座——GBASE全栈数据库的AI融合方案》主题演讲。三、从数据仓库到A…

2026/8/6 2:03:45 阅读更多 →
GBase数据库AI融合方案亮相中国工业软件大会(上)

GBase数据库AI融合方案亮相中国工业软件大会(上)

2026年7月31日,第六届中国工业软件大会在重庆召开。大会以“融AI集智赋能 强应用创启新程”为主题,汇聚国家部委、市级部门、行业协会、科研院所、头部企业、投资机构及媒体代表等500余位嘉宾,共议工业软件与人工智能融合发展新路径。在第六届…

2026/8/6 1:29:04 阅读更多 →

最新新闻

LangChain与LangGraph:大模型应用开发中的工具箱与工作流引擎

LangChain与LangGraph:大模型应用开发中的工具箱与工作流引擎

如果你最近在接触大模型应用开发,大概率听过这两个名字: LangChain 和 LangGraph 。它们经常被一起提及,但很多开发者,尤其是刚入门的同学,会感到困惑:它们到底是什么关系?是同一个框架吗&a…

2026/8/6 11:15:25 阅读更多 →
LTSpice仿真进阶:用真实WAV文件验证音频电路性能

LTSpice仿真进阶:用真实WAV文件验证音频电路性能

1. 项目概述:当电路仿真遇上真实世界的声音 在电路设计的日常里,我们常常需要验证一个音频放大器、一个滤波器,或者一个信号调理电路的实际表现。传统的仿真方法,比如用正弦波、方波作为激励源,固然能验证电路的频率响…

2026/8/6 11:15:25 阅读更多 →
Umi-OCR文字识别:3大核心功能深度解析与实战应用指南

Umi-OCR文字识别:3大核心功能深度解析与实战应用指南

Umi-OCR文字识别:3大核心功能深度解析与实战应用指南 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语言…

2026/8/6 11:15:25 阅读更多 →
重叠相加法:基于FFT的长序列卷积高效实现原理与工程实践

重叠相加法:基于FFT的长序列卷积高效实现原理与工程实践

1. 项目概述:为什么我们需要重叠相加法? 如果你在信号处理领域摸爬滚打过一阵子,尤其是在做实时音频处理、通信系统仿真或者任何需要长序列滤波的场景,大概率会遇到一个经典难题:一个很长的输入信号,要和一…

2026/8/6 11:15:25 阅读更多 →
跳频信号参数估计实战:从时频分析到核心算法解析

跳频信号参数估计实战:从时频分析到核心算法解析

1. 从“猫鼠游戏”到技术核心:为什么跳频信号参数估计如此重要?在无线通信的世界里,有一场持续了数十年的“猫鼠游戏”。一方是通信方,希望自己的信息能安全、可靠地传递;另一方是潜在的干扰者或监听者,试图…

2026/8/6 11:15:25 阅读更多 →
计算机毕业设计之基于Spring Boot的同城宠物服务预约系统的设计与实现

计算机毕业设计之基于Spring Boot的同城宠物服务预约系统的设计与实现

随着新世纪无纸化办公方式的普及,自动化信息处理和基于网络的信息交互方式已被广泛应用。现在很多行业基本上都是交由计算机进行管理和测试,网络与计算机已成为整个线上管理体系中的重要组成部分。虽然信息技术广泛应用和数据存取更加方便,但…

2026/8/6 11:14:24 阅读更多 →

日新闻

深入解析LimboAI C++内核:架构设计与性能优化实战

深入解析LimboAI C++内核:架构设计与性能优化实战

1. 项目概述:为什么我们需要深入LimboAI的C内核?如果你是一名使用Godot引擎的游戏开发者,尤其是对AI行为逻辑有较高要求的项目,那么LimboAI这个名字你大概率不会陌生。它作为Godot 4生态中一个备受瞩目的行为树与状态机插件&#…

2026/8/6 0:00:06 阅读更多 →
Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

1. 项目概述与核心思路大家好,我是老张,一个在游戏开发一线摸爬滚打了十多年的老码农。今天咱们接着聊《空洞骑士》风格2D动作游戏的Demo制作。上一期我们搭好了基础框架,处理了角色移动和碰撞,这一期,我们要让游戏世界…

2026/8/6 0:00:06 阅读更多 →
被动防火门市场前景发展趋势

被动防火门市场前景发展趋势

被动防火门依靠材质结构、密闭构造阻隔烟火蔓延,无需电控启动,是建筑被动消防系统核心构件,行业依托新规管控、城市更新、工业安全升级迎来稳定扩容,整体朝着合规化、专项化、低碳化、智能化方向发展。现阶段 GB12955‑2024 新版国…

2026/8/6 0:00:06 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/5 15:00:43 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/5 13:13:56 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/5 21:00:14 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →