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

并发:使用volatile和不可变性实现线程安全

《Java并发编程实战》中的VolatileCachedFactorizer展示了如何使用volatile和不可变性来实现线程安全。解决了简单缓存实现中可能出现的线程安全问题,同时避免了全量同步带来的性能开销。

场景背景

假设有一个服务(如因数分解服务),需要缓存最近的计算结果以提高效率:

  • 当新请求的参数与缓存中的参数相同时,直接返回缓存结果。
  • 当参数不同时,重新计算并更新缓存。

核心挑战:如何在多线程并发访问时,保证缓存读写的线程安全,同时减少同步开销

代码实现与核心思路

VolatileCachedFactorizer的关键实现如下:

@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {// 用volatile修饰缓存的"不可变结果对象"private volatile ImmutableCache cache = new ImmutableCache(null, null);@Overridepublic void service(ServletRequest req, ServletResponse resp) {BigInteger i = extractFromRequest(req);BigInteger[] factors = cache.getFactors(i);// 缓存未命中,重新计算并更新缓存if (factors == null) {factors = factor(i);// 创建新的不可变对象替换旧缓存cache = new ImmutableCache(i, factors);}encodeIntoResponse(resp, factors);}// 不可变的缓存对象private static class ImmutableCache {private final BigInteger lastNumber;private final BigInteger[] lastFactors;public ImmutableCache(BigInteger lastNumber, BigInteger[] lastFactors) {this.lastNumber = lastNumber;// 防御性拷贝,避免外部修改内部数组this.lastFactors = lastFactors != null ? Arrays.copyOf(lastFactors, lastFactors.length) : null;}// 检查缓存是否命中public BigInteger[] getFactors(BigInteger i) {if (lastNumber == null || !lastNumber.equals(i)) {return null;}// 返回拷贝,避免外部修改内部状态return Arrays.copyOf(lastFactors, lastFactors.length);}}// 其他辅助方法(提取参数、因数分解、编码响应)private BigInteger extractFromRequest(ServletRequest req) { ... }private BigInteger[] factor(BigInteger i) { ... }private void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) { ... }
}

线程安全的核心设计

1. 不可变对象消除了 “写冲突”

ImmutableCache是不可变的(所有成员变量用final修饰,且无修改方法):

  • 一旦创建,其内部状态(lastNumberlastFactors)就无法被修改。
  • 任何 “更新缓存” 的操作,本质上都是创建一个新的ImmutableCache对象,而非修改原有对象。

这就从根本上避免了多线程同时修改同一对象的问题 —— 因为根本没有 “修改” 行为,只有 “替换” 对象引用的操作。

2. volatile 保证了 “读可见性”

cache变量用volatile修饰,确保了:

  • 当一个线程创建新的ImmutableCache并赋值给cache时,这个更新会被立即同步到主内存
  • 其他线程读取cache时,会从主内存获取最新值,而非使用本地缓存的旧值。

因此,线程不会读取到 “过期” 的缓存对象,保证了共享状态的可见性。

3. 无锁设计避免了 “同步竞争”

synchronized等锁机制不同,这个实现:

  • 读取缓存时完全无锁,多个线程可以同时安全访问cache(因为对象不可变,读操作本身不会有冲突)。
  • 更新缓存时仅通过 “创建新对象 + 替换引用” 实现,这个操作是原子的(引用赋值在 Java 中是原子操作)。

虽然可能出现 “多个线程同时计算并覆盖缓存” 的情况(导致临时的重复计算),但这种情况不会破坏线程安全 —— 最终缓存会是某个线程计算的正确结果,且所有线程最终都会看到这个最新结果。

可能的问题与局限性

  • 缓存覆盖问题:如果两个线程同时发现缓存未命中,会同时计算并先后更新缓存,后更新的结果会覆盖先更新的,可能导致短暂的“缓存失效”(但不影响线程安全,只是效率略有损失)。
  • 不适合复杂缓存逻辑:仅适用于“单键单值”的简单缓存场景,无法处理缓存过期、LRU淘汰等复杂策略。
  • 依赖不可变性:若ImmutableCache设计不当(如未做防御性拷贝),则会破坏线程安全性。

文章转载自:

http://8NTDkGIP.hxhrg.cn
http://h2nOzniG.hxhrg.cn
http://hV1IC7ur.hxhrg.cn
http://jZJ8cMZV.hxhrg.cn
http://iUbFAH4j.hxhrg.cn
http://CA1aKjVV.hxhrg.cn
http://Rj68251c.hxhrg.cn
http://ZoeMz8m9.hxhrg.cn
http://bp2rHzoH.hxhrg.cn
http://a3ZHsRGT.hxhrg.cn
http://v6F5XYcj.hxhrg.cn
http://WeEupqLJ.hxhrg.cn
http://lgzwtyRB.hxhrg.cn
http://pzaQSDtF.hxhrg.cn
http://BRzfLeah.hxhrg.cn
http://SzfBcFdh.hxhrg.cn
http://GAT6zvtL.hxhrg.cn
http://9dgywso6.hxhrg.cn
http://oLthg9Dg.hxhrg.cn
http://OGz87jWY.hxhrg.cn
http://wTlhK9Y9.hxhrg.cn
http://dzMWvHwF.hxhrg.cn
http://lzwsmRsO.hxhrg.cn
http://ehEOFVBo.hxhrg.cn
http://h31QSK8L.hxhrg.cn
http://C4oUaNsQ.hxhrg.cn
http://soZ7CC8T.hxhrg.cn
http://Swx4lxdU.hxhrg.cn
http://hNN5ZP39.hxhrg.cn
http://CdyxGg04.hxhrg.cn
http://www.dtcms.com/a/380090.html

相关文章:

  • 【qml入门】在qml项目上加入用户登录qml页面(包含源码)
  • 通义灵码产品演示: 数据库设计与数据分析
  • 大疆图传十公里原理:无人机图传技术解析
  • 【论文阅读】小模型是智能体的未来
  • 空间智能 × 无人机丨考拉悠然携 AI+应急创新产品亮相东盟博览会
  • 【C++】list容器的模拟实现
  • Java学习之——“IO流“的进阶流之打印流的学习
  • Vue 进阶实战:从待办清单到完整应用(路由 / 状态管理 / 性能优化全攻略)
  • 《用 Python 和 TensorFlow 构建你的第一个神经网络:从零开始识别手写数字》
  • 深入探索Vue.js:响应式原理与性能优化
  • 58.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--图形验证码
  • 【Linux】基本指令 · 下
  • springboot+python+uniapp基于微信小程序的旅游服务系统景点信息展示 路线推荐 在线预约 评论互动系统
  • WebApp 的价值与实现:从浏览器架构到用户体验优化
  • 用户体验五大要点:从问题到解决方案的完整指南
  • 从ChatGPT家长控制功能看AI合规与技术应对策略
  • DeepSeek-VL 解析:混合视觉-语言模型如何超越传统计算机视觉方法
  • 从15kHz 到20MHz:为什么LTE带宽不能被子载波间隔整除?
  • Android SystemServer 系列专题【篇五:UserController用户状态控制】
  • Nature | 本周最新文献速递
  • Vuetify:构建优雅Vue应用的Material Design组件库
  • 6种A2A(智能体到智能体)的协议方案
  • 性能测试工具jmeter使用
  • [Windows] PDF 专业压缩工具 v3.0
  • kubectl常用命令
  • MinIO 分布式模式与纠删码
  • linux 宏 DEVICE_ATTR
  • 代码随想录刷题Day56
  • Ansible的 Playbook 模式详解
  • Qt 调用setLayout后,父对象自动设置