深入剖析:为什么多线程下变量会看不见、乱序、不安全?

在 Java 并发编程里,变量看不见、执行乱序、数据不安全是三大经典噩梦,也是面试必问、开发必踩的坑。

很多人只知道用synchronizedvolatileLock,却不知道底层为什么会出现这些问题。一旦理解了本质,你写并发代码会豁然开朗。

这篇文章我会用最通俗、最详细、最底层的方式,把这三个问题一次性讲透:

  1. 为什么多线程下,一个线程修改了变量,另一个线程看不见?(可见性问题)
  2. 为什么代码执行顺序和我写的不一样?(重排序问题)
  3. 为什么多线程同时修改变量,结果会错乱?(原子性 / 线程不安全问题)

一、先铺垫:Java 内存模型(JMM)到底是什么?

要理解三大问题,必须先懂JMM(Java Memory Model)

你可以把它理解成:Java 定义的一套线程与内存交互的规则。

它规定:

  1. 所有变量都存在主内存(Main Memory)
  2. 每个线程有自己的工作内存(Working Memory)
  3. 线程不能直接读写主内存,只能操作自己工作内存里的变量副本

结构如下:

php 复制代码
主内存
↑ ↓
线程1工作内存  ←→ 线程2工作内存

这就埋下了所有并发问题的根源:线程之间不共享工作内存,彼此看不见对方的修改。


二、问题 1:多线程下变量看不见(可见性问题)

现象

线程 A 修改了变量,线程 B永远读不到最新值,甚至陷入死循环。

根本原因

CPU 缓存 + 不及时刷新主存 → 线程之间数据不同步

详细解释:

  1. 线程读取变量 → 复制到线程工作内存(CPU 缓存)
  2. 线程修改变量 → 只改自己缓存里的副本
  3. 不会立刻同步回主内存
  4. 其他线程依旧读自己缓存里的旧值

这就是可见性(Visibility)丢失

结合底层:MESI 协议为什么不能完全解决?

之前我们讲过 MESI 缓存一致性协议,它能让多核 CPU 感知缓存失效。

但 CPU 做了优化:

  • Store Buffer(存储缓冲)
  • Invalidate Queue(失效队列)

为了快,CPU 不会立刻同步缓存,导致短暂时间内,线程依然看不见最新值

代码示例(看不见的经典场景)

java 复制代码
public class VisibilityProblem {
    private static boolean flag = false;

    public static void main(String[] args) {
        // 线程1 等待 flag 变成 true
        new Thread(() -> {
            while (!flag) {
                // 无限循环
            }
            System.out.println("线程1退出");
        }).start();

        // 线程2 修改 flag
        new Thread(() -> {
            try { Thread.sleep(1000); } catch (Exception ignored) {}
            flag = true;
            System.out.println("线程2已修改flag");
        }).start();
    }
}

结果:线程 1 永远死循环,看不见 flag 变成 true。

如何解决?

  • volatile(强制读写主存 + 禁用缓存优化)
  • synchronized
  • Lock

三、问题 2:代码执行乱序(重排序问题)

现象

你写的代码顺序是 A → B → C但 JVM / CPU 实际执行是 B → A → C

为什么要重排序?

为了快! CPU 和 JVM 会在不影响单线程结果的前提下,乱序执行提高效率。

多线程下的灾难

单线程没问题,多线程会直接导致逻辑崩溃

最经典案例:双重检查锁单例(DCL)

java 复制代码
public class Singleton {
    private static Singleton instance; // 没有 volatile

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 重点!!!
                }
            }
        }
        return instance;
    }
}

new Singleton() 会被拆成 3 步:

  1. 分配内存
  2. 初始化对象
  3. instance 指向内存

重排序后可能变成:

  1. 分配内存
  2. instance 指向内存
  3. 初始化对象

这就会出现:instance != null,但对象还没初始化 → 线程使用时直接报错!

重排序的本质

  • 编译器重排序
  • CPU 指令重排序
  • 内存系统重排序

JMM 无法禁止,但可以用 volatile 强制禁止。

volatile 如何禁止重排序?

通过内存屏障(Memory Barrier)

  • 写屏障
  • 读屏障

屏障前后的指令不能跨越屏障重排序


四、问题 3:多线程修改变量不安全(原子性问题)

现象

i++ 看起来是一行代码多线程同时执行,结果永远少算

根本原因

i++ 不是一步,而是三步

  1. 读取 i
  2. 计算 i+1
  3. 写回 i

这三步不是原子操作,线程随时可能切换。

例子

  • 线程 A 读取 i=10
  • 线程 B 也读取 i=10
  • 线程 A 加 1 → 11
  • 线程 B 加 1 → 11
  • 最终结果:11,而不是 12

丢失更新 = 线程不安全

什么是原子性?

一个操作不可分割,要么全部完成,要么全部不做。

为什么 volatile 不能解决原子性?

因为 volatile 只解决:

  • 可见性
  • 禁止重排序

不保证操作不可分割!

正确解决方案

  • synchronized
  • Lock
  • AtomicInteger(CAS 无锁原子)

五、一张图总结三大问题

问题 学名 根本原因 解决方案
变量看不见 可见性问题 CPU 缓存 + 不及时同步主存 volatile、锁
执行乱序 有序性问题 指令重排序 volatile、锁
计算错乱 原子性问题 操作非原子,线程切换 锁、原子类

Java 并发三大特性:可见性、有序性、原子性只要缺一个,就会出 bug。


六、最关键的总结(面试必背)

1. 变量为什么看不见?

  • 线程读写的是自己的缓存副本
  • 修改后没有立刻同步到主内存
  • 其他线程读的是旧副本

2. 为什么代码会乱序?

  • JVM/CPU 为了性能重排序指令
  • 单线程没问题
  • 多线程破坏依赖关系,导致逻辑错误

3. 为什么多线程不安全?

  • 很多操作不是一步完成(如 i++)
  • 线程切换会导致中间状态被覆盖
  • 最终结果丢失、错乱

七、最终结论(最重要)

volatile 只能保证:可见性 + 有序性 不能保证:原子性

要真正安全,必须保证三大特性全部满足:

  • 可见性
  • 有序性
  • 原子性

能同时保证三者的是:

  • synchronized
  • Lock
  • 原子类(AtomicXxx)
相关推荐
志栋智能20 小时前
超自动化安全:实现安全运营现代化的关键
大数据·运维·网络·安全·自动化
代码小书生1 天前
getpass,一个安全输入的 Python 库!
开发语言·python·安全
ylscode1 天前
Pentest Swarm AI:开源群体智能架构如何重构自主渗透测试的边界
网络·安全·安全威胁分析
m0_738120721 天前
渗透测试基础——黑盒测试下的Web漏洞挖掘与利用解析(二)
服务器·前端·python·网络协议·安全·网络安全
一切皆是因缘际会1 天前
底层重构与价值破壁人工智能产业变革
人工智能·安全·重构·系统架构
路baby1 天前
2026第十届御网杯网络安全大赛线上赛 区域赛WP (MISC和Crypto)(详解-思路-脚本)
安全·web安全·网络安全·密码学·ctf·misc·御网杯
zzzsde1 天前
【Linux】线程同步和互斥(5):线程池的实现&&线程安全
linux·运维·服务器·开发语言·算法·安全
消失的旧时光-19431 天前
企业认证与安全体系(五):Spring Security + JWT + Redis 企业级认证实战
redis·安全·spring·spring security·jwt
x-cmd1 天前
[260531] OpenClaw 五月月报:模型接入大爆发、安全重构、手机端终于能当主控台用了 [特殊字符]
安全·ai·智能手机·重构·x-cmd·openclaw
国科安芯1 天前
ASM232S电气特性与TIA/EIA-232-F及ITU V.28标准符合性深度分析
单片机·嵌入式硬件·算法·安全·架构