news 2026/6/15 18:21:12

多线程下的脏读:为什么你的数据“看起来没问题”,结果却全是坑?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多线程下的脏读:为什么你的数据“看起来没问题”,结果却全是坑?

网罗开发(小红书、快手、视频号同名)

大家好,我是展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 前言
    • 什么是多线程下的脏读?
    • 一个最常见、也最危险的场景
      • 场景设定
    • 问题代码:共享变量无锁访问
      • Demo 1:典型脏读示例
      • 你可能会想
    • 为什么这段代码会出问题?
      • `count++` 不是原子操作
      • 可见性问题
      • 脏读在真实项目里长什么样?
    • 解决方案一:synchronized(最直接、最稳)
      • 什么时候该用?
      • Demo 2:使用 synchronized 解决脏读
      • synchronized 到底解决了什么?
      • 代价是什么?
    • 解决方案二:AtomicInteger(高并发下的常用方案)
      • Demo 3:使用 AtomicInteger
      • AtomicInteger 的核心优势
      • 它的局限
    • 解决方案三:LongAdder(超高并发统计神器)
      • Demo 4:使用 LongAdder
      • LongAdder 为什么更快?
      • 注意点
    • 实际项目中该怎么选?
    • 一个真实业务例子
      • 例子:接口 QPS 统计
    • 总结

前言

在日常开发里,只要项目稍微上点规模,多线程几乎是绕不开的。
线程池、异步任务、并发计数、实时统计……
脏读,往往就是这些场景里最容易被忽略、却最容易埋雷的问题之一。

这篇文章我们不讲教科书式定义,直接从真实问题出发,一步一步拆解:

  • 什么是多线程下的脏读
  • 为什么你“只是读个变量”,也会出问题
  • synchronized、AtomicInteger、LongAdder 分别解决了什么问题
  • 实际项目中该怎么选

什么是多线程下的脏读?

先说一句大白话版本:

脏读 = 一个线程读到了另一个线程“还没写完 / 不稳定”的数据

在单线程世界里,这种事根本不可能发生。
但在多线程环境下,只要你共享变量 + 没有同步手段,脏读就是迟早的事。

一个最常见、也最危险的场景

场景设定

假设你在做一个统计功能:

  • 多个线程不断更新一个计数器
  • 另外一个线程负责读取当前统计值并展示

你可能会写出下面这样的代码。

问题代码:共享变量无锁访问

Demo 1:典型脏读示例

publicclassDirtyReadDemo{staticintcount=0;publicstaticvoidmain(String[]args)throwsInterruptedException{Threadwriter=newThread(()->{for(inti=0;i<100_000;i++){count++;}});Threadreader=newThread(()->{for(inti=0;i<100_000;i++){if(count<0){System.out.println("读到了异常值:"+count);}}});writer.start();reader.start();writer.join();reader.join();System.out.println("最终 count = "+count);}}

你可能会想

不就是一个int自增吗?
Java 里 int 读写不是原子的吗?

这里正好是第一个坑

为什么这段代码会出问题?

count++不是原子操作

它实际分成了三步:

  1. 从内存读取 count
  2. +1
  3. 写回内存

多个线程同时执行时,中间状态是可见的

可见性问题

即便你只读不写,如果:

  • 一个线程在修改
  • 一个线程在读
  • 没有任何同步手段

那么读线程看到的值,可能是旧值、半更新值,甚至完全不可预期

脏读在真实项目里长什么样?

在实际业务中,它不会像 Demo 那么“明显”,而是表现为:

  • 偶发统计错误
  • 日志里出现不可能的状态
  • QA 环境永远复现不了,线上偶发

这也是为什么脏读特别难排查

解决方案一:synchronized(最直接、最稳)

什么时候该用?

  • 并发量不大
  • 逻辑复杂
  • 你希望绝对正确

Demo 2:使用 synchronized 解决脏读

publicclassSynchronizedDemo{staticintcount=0;staticfinalObjectlock=newObject();publicstaticvoidmain(String[]args)throwsInterruptedException{Threadwriter=newThread(()->{for(inti=0;i<100_000;i++){synchronized(lock){count++;}}});Threadreader=newThread(()->{for(inti=0;i<100_000;i++){synchronized(lock){intvalue=count;// 这里读取到的一定是一个“完整、一致”的值}}});writer.start();reader.start();writer.join();reader.join();System.out.println("最终 count = "+count);}}

synchronized 到底解决了什么?

  • 保证同一时间只有一个线程访问共享变量
  • 保证原子性 + 可见性

代价是什么?

  • 性能开销
  • 高并发下容易成为瓶颈

解决方案二:AtomicInteger(高并发下的常用方案)

如果你的需求只是:

  • 自增
  • 计数
  • 状态标记

那 AtomicInteger 基本是第一选择

Demo 3:使用 AtomicInteger

importjava.util.concurrent.atomic.AtomicInteger;publicclassAtomicDemo{staticAtomicIntegercount=newAtomicInteger(0);publicstaticvoidmain(String[]args)throwsInterruptedException{Threadwriter=newThread(()->{for(inti=0;i<100_000;i++){count.incrementAndGet();}});Threadreader=newThread(()->{for(inti=0;i<100_000;i++){intvalue=count.get();// 永远不会读到脏数据}});writer.start();reader.start();writer.join();reader.join();System.out.println("最终 count = "+count.get());}}

AtomicInteger 的核心优势

  • 基于 CAS(Compare-And-Swap)
  • 无锁
  • 性能远好于 synchronized

它的局限

  • 只适合简单数值操作
  • 多个变量组合逻辑不适合

解决方案三:LongAdder(超高并发统计神器)

当你面对的是:

  • 高并发写
  • 偶尔读
  • 对实时精确度要求不极端

LongAdder会非常香。

Demo 4:使用 LongAdder

importjava.util.concurrent.atomic.LongAdder;publicclassLongAdderDemo{staticLongAddercounter=newLongAdder();publicstaticvoidmain(String[]args)throwsInterruptedException{Threadwriter=newThread(()->{for(inti=0;i<100_000;i++){counter.increment();}});Threadreader=newThread(()->{for(inti=0;i<100_000;i++){longvalue=counter.sum();}});writer.start();reader.start();writer.join();reader.join();System.out.println("最终 count = "+counter.sum());}}

LongAdder 为什么更快?

  • 内部拆分成多个 Cell
  • 不同线程更新不同 Cell
  • 读的时候再汇总

注意点

  • sum()不是强一致的瞬时值
  • 适合统计类场景,不适合金融级精度

实际项目中该怎么选?

我给你一个非常工程化的选择建议

场景推荐方案
逻辑复杂,强一致synchronized
简单计数,高并发AtomicInteger
超高并发统计LongAdder
多变量事务synchronized / Lock

一个真实业务例子

例子:接口 QPS 统计

  • 每个请求进来都 +1
  • 定时上报监控系统
  • 不要求每一毫秒都精准

LongAdder 是最合适的选择

总结

多线程下的脏读,不是“写错代码”,而是“没意识到并发模型”

  • 没锁 ≠ 性能高
  • 原子类 ≠ 万能
  • LongAdder ≠ 精准计数

理解每种工具解决了什么问题、牺牲了什么,比死记 API 更重要。

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

adb卸载手机app

一、下载adb Windows版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-windows.zip Mac版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-windows.zip Linux版本&#xff1a;https://dl.google.com/android/r…

作者头像 李华
网站建设 2026/6/15 15:54:32

基于STM32的智能花盆系统设计与实现

基于STM32的智能花盆系统设计与实现 摘要 本文设计并实现了一种基于STM32F407VET6微控制器的智能花盆系统&#xff0c;通过多传感器融合与智能控制策略&#xff0c;实现了对植物生长环境的全方位监测与精准调控。系统集成DS18B20温度传感器、土壤湿度传感器、光敏电阻、雨滴传…

作者头像 李华
网站建设 2026/6/15 15:58:12

2025年华南师范大学计算机考研复试机试真题(附 AC 代码 + 解题思路)

2025年华南师范大学计算机考研复试机试真题 2025年华南师范大学计算机考研复试上机真题 历年华南师范大学计算机考研复试上机真题 历年华南师范大学计算机考研复试机试真题 更多学校题目开源地址&#xff1a;https://gitcode.com/verticallimit1/noobdream N 诺 DreamJudg…

作者头像 李华
网站建设 2026/6/15 13:26:19

leetcode热题岛屿数量

给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外&#xff0c;你可以假设该网格的四条边均被…

作者头像 李华
网站建设 2026/6/15 14:11:13

【图像处理基石】什么是神经渲染?

前言 在计算机图形学和计算机视觉的交叉领域&#xff0c;神经渲染&#xff08;Neural Rendering&#xff09;正成为最热门的研究方向之一。它打破了传统渲染依赖手工设计规则的局限&#xff0c;用神经网络学习从数据到图像的映射&#xff0c;让“AI画画”“数字人重建”“场景生…

作者头像 李华
网站建设 2026/6/14 13:28:00

性能测试中关于硬件环境的测试

比如营销活动的服务器的部署规格&#xff1a; 部署规格 CPU&#xff1a;0.001~4 (Core) 内存&#xff1a;16384~16384(Mb) 6个POD测试要点&#xff1a;单个pod的性能指标摸底&#xff0c;6Pod 集群峰值容量测试&#xff0c;6Pod 集群 72 小时稳定性测试&#xff0c;6Pod 集群容…

作者头像 李华