目录

Volatile

# 变量可见性

每个线程会有各自的线程栈,可以理解为有各自的工作内存,在线程之外还有一个共享的主内存,默认不加 volatile 关键字的变量,JVM 不会保证把变量读取到主内存去。

  1. 不加 volatile 的变量,读取和更新只影响了当前线程的工作内存,其他线程对变量改变不可见。
  2. 加了 volatile 的变量,在读取时会先从主内存中加载数据到主内存中。在更新变量时,更新工作内存的同时会去更新主内存(怎么保证一致性的?通过 CPU 的缓存一致性方案解决(MESI),每个线程有自己的存储空间在更新到主存时依赖 CPU 的缓存一致性协议来保证数据一致性)

处理器Cache模型

  1. 加了 volatile 的变量,在同一线程更新此变量时,会同时把非 volatile 变量刷新到主内存中去。(如果一个线程更新了 volatile 变量 和 非 volatile 变量,另一个线程只更新了非 volatile 变量,怎么保证这个非 volatile 变量的一致性?)

# 指令重排

  1. CPU 为了提高执行效率,会把没有依赖逻辑的变量操作进行指令重排(或者说是执行顺序的不确定性)。(指令重排可能发生在两个环节:1. Java编译阶段,转换为字节码的阶段。2. CPU 执行阶段)

  2. As-if-serial 语义是,所有的动作(Action)都可以为了优化而被重新排序,但是必须保证重新排序后的结果和程序代码本身应有的结果一致。

    int a = 1;
    int b = 2;
    int c = a + b;
    // 将上面代码转为字节码或者机器指令,可以展开以下几步
    // 1. 对 a 进行赋值
    // 2. 对 b 进行赋值
    // 3. 取 a 的值
    // 4. 取 b 的zhi
    // 5. 将取到的两个值相加后存入 c
    

    在上面的 5 个动作中, 动作 1 可能会和动作 2、4 重排序,动作 2 可能会和动作 1、3 重排序,动作3可能会和动作2、4重排序,动作4可能会和1、3重排序。但动作1和动作3、5不能重排序。动作2和动作4、5不能重排序。因为它们之间存在数据依赖关系,一旦重排,as-if-serial语义便无法保证。

# Happens-Before

# 何时使用 volatile 关键字

# 相关文档

JVM官方文档 (opens new window)

Java并发编程:volatile关键字解析 (opens new window)

Guide to the Volatile Keyword in Java (opens new window)

上次更新: 2024/11/05, 03:15:29