Java volatile 深度解析

简介

被 volatile 修饰的变量有两大特点:

  • 当写一个 volatile 变量时,JMM 会把线程对应的本地内存中的共享变量值立即刷新回主内存中。
  • 当读一个 volatile 变量时,JMM 会把线程对应的本地内存设置为无效,需要工作线程重新回到主内存中读取最新共享变量。

所以 volatile 的写的内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。

JMM 全称为 Java Memory Model 本身是一种抽象的概念并不真实存在,它仅仅描述的是一组约定或规范,通过这组规范定义了 Java 程序中(尤其是多线程)各个变量的读写访问方式。关键技术点都是围绕多线程的原子性、可见性和有序性展开的。

内存屏障

内存屏障,也称为内存栅栏或栅栏指令,是一类同步屏障指令,是 CPU 或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作,避免代码重排序。即内存屏障之前的所有写操作都要回写到主内存中,内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果。

重排序是指编译器和处理器为了优化程序性能而对指令进行重新排序的一种手段,有时候会改变程序语句的执行先后顺序。通常只有不存在数据依赖关系才可以重排序,若存在数据依赖关系禁止重排序!

内存屏障大致分为两种:

  • 读屏障(Load Barrier):在读指令之前插入读屏障,让工作内存或 CPU 高速缓存当中的缓存数据失效,重写回到主内存中获取最新数据。
  • 写屏障(Store Barrier):在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中。

Java 中的内存屏障其实就是一种 JVM 指令,JMM 的重排序规则会要求 Java 编译器在生成 JVM 指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile 实现了 Java 内存模型中的可见性和有序性(禁重排),但 volatile 无法保证原子性。

在 Java 中,对于 volatile 变量的写操作,JVM 会在写操作之前添加一个 StoreStore 屏障,保证前面的所有写操作都已经刷新到主内存且禁止前面的其它写操作和后面的 volatile 写重排序。会在 volatile 写操作之后添加一个 StoreLoad 屏障,保证前面 volatile 写操作数据已经刷新到主内存且禁止上面的 volatile 写和下面的其它读操作重排序。

在 Java 中,对于 volatile 变量的读操作,JVM 会在读操作之后添加 LoadLoad 屏障和 LoadStore 屏障,LoadLoad 屏障来禁止上面的 volatile 读操作和下面的其它读操作重排序。LoadStore 屏障来禁止上面的 volatile 读操作和下面的其它写操作重排序。

posted @ 2024-07-09 17:53  劣技砖猿  阅读(8)  评论(0编辑  收藏  举报