news 2026/6/24 18:19:15

Java中 count++ 不是原子操作的核心原理解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java中 count++ 不是原子操作的核心原理解析

核心问题:count++ 不是原子操作

count++看起来是一行代码,但实际对应3个CPU指令

// Java代码count++;// 实际执行的CPU指令:1.读取count的当前值到CPU寄存器(read)2.把寄存器的值加1(add)3.把新值写回内存(write)

场景模拟:两个线程同时执行 count++

假设volatile int count = 0;

时间线 | 线程A的操作 | 线程B的操作 | 内存中count的值 ------|--------------------------|--------------------------|---------------- 1 | read: count=0 → 寄存器A=0 | | 0 2 | | read: count=0 → 寄存器B=0 | 0 3 | add: 寄存器A=0+1=1 | | 0 4 | | add: 寄存器B=0+1=1 | 0 5 | write: 写回count=1 | | 1 6 | | write: 写回count=1 | 1(应该是2!)

结果:两个线程都做了+1操作,但最终结果是1而不是2。

volatile 在这里起了什么作用?

volatile只保证了:

  1. 当线程A在第5步写入count=1时,立即刷新到主内存
  2. 当线程B读取时,能读到最新的值(如果它重新读的话)

但问题是:线程B在第2步已经读过了(读取的是0),不会再重新读!

更详细的时序图

线程A: 读count(0) → 计算1 → 写count(1,立即刷新) ↑ ↓ 内存count: 0 ← 冲突 → 1 ↑ ↓ 线程B: 读count(0) → 计算1 → 写count(1,覆盖了A的结果)

volatile能解决的场景 vs 不能解决的场景

volatile能解决的(一写多读)

// 线程A(写线程)publicvoidwrite(){flag=true;// 单个写操作data=100;}// 线程B、C、D(读线程)publicvoidread(){if(flag){// 一定能看到trueSystem.out.println(data);// 一定能看到100}}

volatile不能解决的(多写)

// 线程A、B、C都执行这个:publicvoidincrement(){count++;// 问题在这里!这个操作是"读-改-写"三部曲// 相当于:// int temp = count; // 1.读(可能读到旧值)// temp = temp + 1; // 2.改(在各自线程中改)// count = temp; // 3.写(会相互覆盖)}

类比解释

想象一个共享的记事本(内存)和三个人(线程):

场景1:单写多读(volatile有效)

  • 只有小明可以在记事本上写
  • 小红和小刚只能看
  • 当小明更新了内容,小红和小刚立刻能看到新内容 ✅

场景2:多写(volatile无效)

  • 小明、小红、小刚都可以在记事本上写
  • 当前记事本写着:库存=10
  • 小明:看到库存=10,计算10-1=9,准备写9
  • 小红:同时看到库存=10,计算10-1=9,准备写9
  • 小明:写下9
  • 小红:也写下9(覆盖了小明的9)

结果:卖了2件商品,库存应该是8,但实际是9 ❌

为什么AtomicInteger能解决?

AtomicIntegercount=newAtomicInteger(0);// count.incrementAndGet() 内部原理:publicfinalintincrementAndGet(){intprev,next;do{prev=get();// 1.读取当前值next=prev+1;// 2.计算新值}while(!compareAndSet(prev,next));// 3.比较并交换returnnext;}

关键在第3步compareAndSet(prev, next)意思是:

  • “如果现在内存中的值还是我刚刚读到的prev,我就把它改成next”
  • “如果内存中的值已经被别人改了,那我就重试”

用记事本的例子:

  1. 小明看到库存=10,想改成9
  2. 小红也看到库存=10,想改成9
  3. 小明先写:检查发现确实是10,成功改成9
  4. 小红再写:检查发现现在是9(不是10了),于是重新读、重新计算(9-1=8),再写 ✅

总结要点

  1. volatile只管"看到最新值",不管"正确修改值"
  2. 多线程修改的核心矛盾:多个线程基于同一个旧值做计算,然后相互覆盖
  3. 解决方案本质:需要把"读-改-写"这三步变成一个不可分割的原子操作
  4. Atomic类用CAS(比较并交换)实现原子性
  5. synchronized用互斥锁实现原子性

总之,关键是要认识到count++不是一步操作,而是三步,而volatile只保证了每一步内部的内存可见性,但没有保证这三步作为一个整体的原子性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 19:37:40

YOLO模型镜像集成Wandb监控,实时查看GPU训练状态

YOLO模型镜像集成Wandb监控,实时查看GPU训练状态 在工业视觉、自动驾驶和智能安防等高实时性场景中,目标检测模型的训练不再只是“跑通就行”的实验行为,而是需要精细化管理的研发工程。YOLO系列凭借其单次推理完成检测的能力,早已…

作者头像 李华
网站建设 2026/6/19 1:48:19

专业照明如何通过核心参数提升商业空间氛围与体验

于商业空间的设计跟运营里头,照明可不单单是提供基础光亮而已,它深切地影响到空间氛围,影响商品呈现,影响顾客心理,甚至影响最终的消费决策。有一套专业且适配的商业照明系统,它能够悄然无声地提升品牌形象…

作者头像 李华
网站建设 2026/6/22 5:18:23

YOLOv8-DyHead动态头机制解析:自适应空间特征校准

YOLOv8-DyHead动态头机制解析:自适应空间特征校准 在工业质检、智能交通和无人机巡检等现实场景中,目标的尺度变化剧烈、背景干扰复杂,且常伴随遮挡与密集排列。传统目标检测模型在这些条件下往往表现受限——小目标漏检、定位模糊、分类混淆…

作者头像 李华
网站建设 2026/6/20 5:00:09

(10-2)基于MCP实现的多智能体协同系统:通用模块

10.3 通用模块在本项目中,“utils ”目录是Agent-MCP项目的通用模块,提供了跨模块复用的辅助功能,为框架其他组件提供基础支撑。其核心作用包括封装 Git 操作(如代码提交、分支管理)以支持代理的版本控制需求&#xf…

作者头像 李华
网站建设 2026/6/18 23:26:53

基于SpringBoot + Vue的高校校园智能考勤系统

文章目录 前言一、详细操作演示视频二、具体实现截图三、技术栈1.前端-Vue.js2.后端-SpringBoot3.数据库-MySQL4.系统架构-B/S 四、系统测试1.系统测试概述2.系统功能测试3.系统测试结论 五、项目代码参考六、数据库代码参考七、项目论文示例结语 前言 💛博主介绍&a…

作者头像 李华
网站建设 2026/6/15 21:05:05

YOLOv9-SwinTransformer混合架构测试中,GPU显存压力大

YOLOv9-SwinTransformer混合架构测试中,GPU显存压力大 在当前智能视觉系统不断向高精度、强泛化能力演进的背景下,工业界对目标检测模型的要求早已不再局限于“快”或“准”的单一维度。以YOLO系列为代表的高效单阶段检测器,正越来越多地与Sw…

作者头像 李华