news 2026/5/8 7:00:48

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/5/2 8:56:24

YOLO在自动驾驶中的应用突破,背后需要多少GPU算力支撑?

YOLO在自动驾驶中的应用突破,背后需要多少GPU算力支撑? 在智能驾驶系统从辅助功能迈向全场景自主决策的今天,环境感知能力正成为决定技术成败的关键瓶颈。尤其是对行人、车辆、交通标志等目标的实时识别,不仅要求高精度&#xff0…

作者头像 李华
网站建设 2026/5/6 8:43:17

YOLO家族全面解析:从科研到生产部署的完整路径指南

YOLO家族全面解析:从科研到生产部署的完整路径指南 在智能制造工厂的一条高速SMT贴片线上,每分钟有超过500块PCB板通过光学检测仪。传统基于规则的视觉系统面对日益复杂的焊点缺陷形态时频频“失明”——它无法识别新型短路模式,也无法适应不…

作者头像 李华
网站建设 2026/5/4 19:53:59

7款AI论文神器实测:30分钟生成万字,知网查重轻松过!

在学术写作的战场上,时间紧、任务重、查重严是大学生、研究生、科研人员的三大痛点。想象一下——喝杯咖啡的功夫,一篇结构完整、逻辑严谨、查重率低于5%的万字论文初稿就搞定了,导师修改意见秒响应,投稿一次过,高分稳…

作者头像 李华
网站建设 2026/5/4 19:23:33

YOLO目标检测实战:如何用最低Token消耗跑通高精度推理

YOLO目标检测实战:如何用最低Token消耗跑通高精度推理 在AI服务按Token计费的今天,一张图像识别动辄消耗上千Token,让许多企业望而却步。尤其是工业质检、安防监控这类高频调用场景,若依赖GPT-4V等多模态大模型,月成本…

作者头像 李华
网站建设 2026/4/26 7:04:23

YOLOv9-EfficientRep重参数化卷积详解

YOLOv9-EfficientRep重参数化卷积详解 在智能制造车间的视觉质检线上,一台搭载边缘计算盒子的工业相机正以每秒30帧的速度扫描PCB板。系统需要在15毫秒内完成缺陷检测并触发分拣动作——这对目标检测模型提出了严苛要求:既要高精度识别微米级焊点虚焊&am…

作者头像 李华
网站建设 2026/4/18 1:43:57

Prebuild tools

预构建工具(Prebuild tools) 原生插件的分发与其实现同等重要。要安装原生插件,必须确保所有必要的依赖项已安装并配置妥当(详见「环境搭建(setup)」章节)。终端用户执行 npm install 时需要编…

作者头像 李华