当前位置: 首页 > news >正文

经典的Java双重检查锁代码

文章目录

  • 前言
    • 📐 一、整体逻辑
    • ⏰ 二、为什么提前 5 分钟?
    • 🔒 三、双重检查锁详解
      • 3.1 为什么要用双重检查?
      • 3.2 执行流程图解
    • 💡 四、`volatile` 关键字的作用(关键!)
      • 4.1 volatile 的两个作用
      • 4.2 为什么必须加 volatile?
    • 🎯 五、为什么这样设计?
      • 5.1 三种方案对比
      • 5.2 配合定时任务
    • 📊 六、总结这段代码的知识点
    • 🎓 七、学习建议

前言

经典的双重检查锁代码!这是 Java 并发编程的绝佳学习案例!


📐 一、整体逻辑

publicStringgetAccessToken(){// 步骤 1:第一次检查(不加锁)if(System.currentTimeMillis()>expireTime-5*60*1000){// 步骤 2:加锁synchronized(this){// 步骤 3:第二次检查(加锁后再次检查)if(System.currentTimeMillis()>expireTime-5*60*1000){// 步骤 4:真正刷新 tokenrefreshToken();}}}returnaccessToken;}

⏰ 二、为什么提前 5 分钟?

原因说明
缓冲时间避免在 token 即将过期的临界点出问题
网络延迟刷新 token 可能需要网络请求,提前刷新确保安全
双重保险定时任务是 110 分钟(提前 10 分钟),代码里又提前 5 分钟,双重保险

🔒 三、双重检查锁详解

3.1 为什么要用双重检查?

问题说明
性能问题如果每次都加锁,高并发时会成为瓶颈
线程安全不加锁可能导致多个线程同时刷新

3.2 执行流程图解

假设有 3 个线程同时调用:

时间线: ├─ 线程 A 进入,第一次检查:需要刷新 ├─ 线程 A 获取锁 ├─ 线程 B 进入,第一次检查:需要刷新 ├─ 线程 B 等待锁(被阻塞) ├─ 线程 A 第二次检查:需要刷新 → 刷新 token ├─ 线程 A 释放锁 ├─ 线程 B 获取锁 ├─ 线程 B 第二次检查:token 已刷新!不刷新! ✅

💡 四、volatile关键字的作用(关键!)

看第 35、38 行:

privatevolatileStringaccessToken;// ⚠️ 注意 volatile!privatevolatilelongexpireTime=0;

4.1 volatile 的两个作用

作用说明
1. 可见性一个线程修改,其他线程立刻看到
2. 禁止指令重排序防止 CPU 对指令重新排序

4.2 为什么必须加 volatile?

如果没有 volatile,会有什么问题?

// 伪代码:refreshToken 里的操作this.accessToken=newToken;// 步骤 1this.expireTime=newExpireTime;// 步骤 2

没有 volatile 时,CPU 可能会重排序:

this.expireTime = newExpireTime; // 步骤 2(先执行) this.accessToken = newToken; // 步骤 1(后执行)

这就导致:

  • 线程 A 刚把expireTime设置为新值,但accessToken还没更新
  • 线程 B 看到expireTime是新的,就直接返回accessToken,但accessToken还是旧的!❌

🎯 五、为什么这样设计?

5.1 三种方案对比

方案优点缺点
每次都刷新简单性能差,微信 API 调用频繁
每次都加锁线程安全性能差,高并发时瓶颈
✅ 双重检查锁性能好 + 线程安全实现稍复杂

5.2 配合定时任务

// 定时任务:每 110 分钟刷新一次scheduler.scheduleAtFixedRate(this::refreshToken,110,110,TimeUnit.MINUTES);

定时任务 + 双重检查 = 双保险!

  • 正常情况下:定时任务自动刷新
  • 异常情况下:getAccessToken()主动刷新

📊 六、总结这段代码的知识点

知识点位置说明
volatile第 35、38 行保证可见性,禁止指令重排
双重检查锁第 79-84 行性能与线程安全的平衡
synchronized第 80 行保证原子性
时间计算第 79 行expireTime - 5分钟提前过期

🎓 七、学习建议

  1. 先理解单线程版本:不考虑并发,先写出简单版本
  2. 再理解同步版本:每次都加锁,保证线程安全
  3. 最后理解双重检查锁:在同步版本基础上优化性能
  4. 学习 volatile:看《Java 并发编程实战》第 3 章

http://www.jsqmd.com/news/774016/

相关文章:

  • 一文带你搞懂现在爆火的RAG,究竟是什么
  • 专业的企业官网搭建怎么选?别再踩坑了!从技术底层拆解微加AI如何保底护航
  • POD 定制耗时费力?凌风工具箱批量操作,高效搞定全套定制设置
  • ORA富集分析避坑指南:为什么你的通路结果总是不显著?可能是这4个参数没设对
  • 【国家级AI安全强制标准前哨】:AISMM如何定义“可验证智能”?3大不可绕过的技术红线与2025Q4企业自检清单
  • Turnitin升级后查AI率更严!英文论文AI率 88%降到*%,5个降AI方法亲测有效
  • 工业自动化控制板上,隔离RS-485收发器如何护航总线通信?
  • 工业级高密度电力配置预算与可靠性平衡路径解析
  • 基于Unity的虚拟人开发:从架构解析到实战部署
  • 渗透测试实战(一):文件传输全技法与深度解析
  • Arm Cortex-X2微架构异常分析与安全防护实践
  • 一个公式,解释了我见过的大部分冲突
  • 基于MCP协议打通Figma与Cursor:AI驱动的设计与代码同步实践
  • XYBot V2微信机器人:插件化架构解析与从零部署实战
  • 从C++ 14到C++ 17:理解聚合初始化是如何工作的
  • 基于RAG与向量检索的Claude长上下文管理工具解析
  • AI赋能API调试:构建智能错误诊断与性能监控插件
  • 企业级无人机管理平台架构设计|多行业巡检适配,支持内网私有化与二次开发
  • AI智能体技能库:模块化设计、核心技能与实战集成指南
  • Arm Cortex-A720缓存与TLB底层访问机制解析
  • 乘风破浪,扬帆出海,智慧新物流(Logistics)之仓储领域,项目管理相关术语集结
  • ChatClaw:基于智能体与网页抓取的AI信息增强框架实践
  • 《信息系统项目管理师教程(第4版)》——信息技术发展
  • ESP32-C5开发板多协议无线与低功耗设计解析
  • Claude 不订阅也能用:国产模型直连实操,我替你踩完了所有坑
  • 多模式夹持天线系统(PASS)原理与毫米波通信应用
  • 探讨(HAL库)使用rs485时,开了DMA收发,但是没有开串口的全局中断,导致只发送(接收)了一次数据就不发送了。
  • 架构师视角:如何构建支持GB28181/RTSP的异构AI视频平台?从Docker部署到源码交付的深度实践
  • 2026年知名的连续式烘干机/大型烘干机定制加工厂家推荐 - 品牌宣传支持者
  • 别再花冤枉钱买涂色本了!扣子(Coze)+GPT-Image-2一键生成,想要什么画就有什么画