0

0

如何解决Java并发下的缓存击穿问题_多线程互斥加载方案

P粉602998670

P粉602998670

发布时间:2026-03-06 10:25:00

|

567人浏览过

|

来源于php中文网

原创

缓存击穿的核心是多个线程重复加载同一数据,应优先用redis分布式锁(如set nx px)控制加载权,并配合双重检查与redisson可重入锁;加载逻辑需轻量、带超时与异常清理,空值缓存仅限明确不存在场景且ttl设2–5秒;本地+分布式缓存需同步清理或弃用本地缓存以保一致性。

如何解决java并发下的缓存击穿问题_多线程互斥加载方案

缓存击穿发生时,get 方法返回 null 但多个线程同时触发加载

这是最典型的击穿表现:缓存过期瞬间,大量请求穿透到数据库,DB 压力陡增。关键不是“没缓存”,而是“多个线程重复加载同一份数据”。单纯加 synchronized 在方法上会锁住整个实例,吞吐暴跌;用 ConcurrentHashMapcomputeIfAbsent 又只对本地缓存有效,跨 JVM 失效。

  • 优先用分布式锁(如 Redis 的 SET key value NX PX 30000)控制加载权,而不是锁业务代码
  • computeIfAbsent 可以用,但必须配合双重检查:先查本地缓存 → 查不到再抢分布式锁 → 抢到才查 DB 并写缓存
  • 避免在锁内做耗时操作,比如远程调用、复杂计算;加载逻辑要轻量,失败需清理锁并重试机制

Redisson 实现可重入、自动续期的分布式锁

RedissonRLock 能解决手动实现锁的续期、死锁、释放异常等问题。但默认不支持“锁等待超时”和“加载超时”的分离——你得自己控制加载任务的执行时限,否则一个慢查询可能让几十个线程卡在 lock.lock() 上。

  • tryLock(3, 10, TimeUnit.SECONDS):最多等 3 秒抢锁,持有锁最长 10 秒(自动看门狗续期)
  • 加载逻辑必须包裹在 try/finally 中,确保 unlock() 执行,哪怕抛异常
  • 不要把 RLock 存为类成员变量,每次都要从 RedissonClient 获取新实例,否则锁状态会混乱

加载失败后,要不要设置空值缓存?

空值缓存(setex key 60 "null")能防穿透,但容易掩盖真实问题:比如 DB 连接池满、下游服务不可用。如果每次加载都失败,空值会把错误固化 60 秒,用户看到的全是“暂无数据”。

九歌
九歌

九歌--人工智能诗歌写作系统

下载
  • 仅对明确的“数据不存在”(如查不到用户 ID)设空值,且 TTL 缩短至 2–5 秒
  • 对异常失败(SQLExceptionTimeoutException),不写空值,改用熔断或降级策略
  • 监控 cache.null.hit 指标,突增说明上游数据层出问题,不是缓存配置问题

本地缓存 + 分布式缓存组合时,Double-Check 容易漏掉更新场景

比如用 Caffeine 做本地缓存,Redis 做共享缓存。常见写法是:先查 Caffeine → 查不到再查 Redis → Redis 也没才加载。但更新时只清 Redis,本地缓存还留着脏数据,其他节点读不到最新值。

立即学习Java免费学习笔记(深入)”;

  • 更新操作必须同步清理所有节点的本地缓存,可用 Redis 的 PUBLISH 通知或引入 Cache2k 的事件监听
  • 或者干脆放弃本地缓存,用 Redis 的 LRU 配合连接池优化,省去一致性维护成本
  • 如果坚持用本地缓存,refreshAfterWriteexpireAfterWrite 更安全,它允许旧值继续服务,后台异步刷新

真正难的不是加锁或设空值,而是判断“这次加载到底该不该阻塞后续请求”——有些业务能接受短暂陈旧数据,有些必须强一致。方案得跟着读写比例、容忍延迟、故障恢复能力一起调,不是套个 RLock 就完事。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

404

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

249

2023.10.07

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

252

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1029

2024.03.01

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

294

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

105

2025.10.23

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

743

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

375

2025.12.24

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

19

2026.03.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.1万人学习

C# 教程
C# 教程

共94课时 | 10.7万人学习

Java 教程
Java 教程

共578课时 | 77.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号