结论:原子性指一个操作要么完整发生要么完全不发生,中间状态不可见;可见性指一个线程的写何时被其他线程看到;有序性指指令执行顺序与代码顺序一致。编译器指令重排、CPU 乱序执行与多级缓存会分别从三个维度破坏朴素预期,内存屏障和语言内存模型(C++11/Java/Go 的 happens-before)是恢复保证的手段。

展开:破坏来源逐个看:1)原子性——i++ 是读-改-写三步,两个线程并发执行可能丢更新;64 位变量在 32 位机上可能被拆成两次写(tearing)。2)可见性——CPU 各核有私有 L1/L2 缓存,核 A 的写可能滞留在 store buffer 里,核 B 读到的仍是旧值。3)有序性——编译器为优化可交换无依赖语句;CPU 的乱序执行和写缓冲/失效队列让内存操作实际生效顺序不同于程序顺序(经典反例:store-load 乱序导致 Dekker 算法失效)。解法分层:硬件提供 MFENCE/锁前缀指令;语言层提供 atomic 与内存序(relaxed/acquire/release/seq_cst);工程实践优先用互斥锁等高级原语——锁的获取/释放隐含 acquire/release 语义,顺带解决三个问题。易错点:volatile(Java 语义)保证可见性和有序性但不保证原子性,volatile i++ 仍是错的;双重检查锁单例在 C++/Java 旧内存模型下必须用原子/volatile 修正。

// 正确的双重检查锁
private static volatile Instance inst;
if (inst == null) {
    synchronized (X.class) {
        if (inst == null) inst = new Instance(); // volatile 防止构造指令重排
    }
}

追问方向:MESI 缓存一致性协议与内存屏障的关系、x86 的 TSO 与 ARM 的弱内存模型差异(为什么 x86 上跑对的代码 ARM 上崩)。