Java volatile四种内存屏障的作用与生效机制原理详解

 更新时间:2025年09月10日 11:15:36   作者:亲爱的非洲野猪  
内存屏障是处理器提供的一种指令,用于控制指令执行顺序和内存可见性,在Java中,volatile关键字就是通过插入内存屏障来实现其内存语义的,下面我将详细解释四种内存屏障的含义和工作原理,感兴趣的朋友一起看看吧

在Java中,volatile关键字是一种轻量级的同步机制,用于确保变量的可见性和有序性。为了实现这些功能,Java虚拟机(JVM)在底层使用了内存屏障(Memory Barrier),这些内存屏障确保了在多线程环境下,对共享变量的读写操作的正确顺序和可见性。

内存屏障(Memory Barrier)是处理器提供的一种指令,用于控制指令执行顺序和内存可见性。在Java中,volatile关键字就是通过插入内存屏障来实现其内存语义的。下面我将详细解释四种内存屏障的含义和工作原理。

1. 四种基本内存屏障

1.1 StoreStore屏障

作用

  • 确保屏障前的所有普通写操作(store)完成并刷新到主内存
  • 在屏障后的volatile写操作之前执行

生效机制

普通写操作1
普通写操作2
StoreStore屏障
volatile写操作

实际效果:保证在volatile变量写入前,所有之前的普通变量写入都已经完成并可见

1.2 StoreLoad屏障

作用

  • 确保屏障前的所有写操作(包括volatile写)完成并刷新到主内存
  • 在屏障后的所有读操作(包括volatile读)之前执行

生效机制

volatile写操作
StoreLoad屏障
volatile读操作/普通读操作

实际效果:这是最"重量级"的屏障,会使该屏障之前的所有内存访问指令(存储和装载)完成之后,才执行该屏障之后的内存访问指令

1.3 LoadLoad屏障

作用

  • 确保屏障前的所有读操作(load)完成
  • 在屏障后的所有读操作之前执行

生效机制

volatile读操作
LoadLoad屏障
普通读操作/volatile读操作

实际效果:保证在读取后续变量前,先完成对volatile变量的读取

1.4 LoadStore屏障

作用

  • 确保屏障前的所有读操作(load)完成
  • 在屏障后的所有写操作之前执行

生效机制

volatile读操作
LoadStore屏障
普通写操作/volatile写操作

实际效果:保证在写入任何变量前,先完成对volatile变量的读取

2. 内存屏障在volatile中的具体应用

2.1 volatile写操作的内存屏障插入

编译器会在volatile写操作前后插入以下屏障:

[普通写操作]
StoreStore屏障
[volatile写操作]
StoreLoad屏障

示例

x = 42;       // 普通写
y = true;     // volatile写

实际生成的指令序列:

store x, 42
StoreStore屏障
store y, true
StoreLoad屏障

2.2 volatile读操作的内存屏障插入

编译器会在volatile读操作前后插入以下屏障:

LoadLoad屏障
[volatile读操作]
LoadStore屏障

示例

if (y) {      // volatile读
    z = x;    // 普通读和普通写
}

实际生成的指令序列:

LoadLoad屏障
load y
LoadStore屏障
load x
store z, x

3. 内存屏障如何保证happens-before关系

内存屏障通过限制处理器和编译器的重排序来建立happens-before关系:

  1. StoreStore屏障:确保volatile写之前的普通写操作happens-before volatile写
  2. StoreLoad屏障:确保volatile写happens-before后续的volatile读/写
  3. LoadLoad屏障:确保volatile读happens-before后续的所有读操作
  4. LoadStore屏障:确保volatile读happens-before后续的所有写操作

4. 实际处理器中的实现差异

不同处理器架构对内存屏障的支持不同:

  • x86/64:原生支持较强的内存模型,只有StoreLoad屏障是真正有作用的
  • ARM/PowerPC:需要显式使用所有四种屏障
  • JVM:会根据目标平台将Java内存屏障映射到具体的处理器指令

例如,在x86上:

  • StoreStore屏障通常实现为空操作(no-op)
  • StoreLoad屏障实现为mfence指令或lock前缀指令

5. 示例分析

class ReorderingExample {
    int x = 0;
    volatile boolean v = false;
    void writer() {
        x = 42;      // 普通写
        v = true;    // volatile写
    }
    void reader() {
        if (v) {     // volatile读
            System.out.println(x); // 普通读
        }
    }
}

内存屏障插入后的执行顺序保证

  • 在writer()中:
    • x = 42v = true之间插入StoreStore屏障
    • 确保x的写入在v的写入前完成并可见
  • 在reader()中:
    • if (v)前插入LoadLoad屏障
    • System.out.println(x)前插入LoadStore屏障
    • 确保读取v后才读取x,且读取的是最新值

6. 为什么需要四种屏障

四种屏障对应不同的读写组合,提供了细粒度的控制:

  1. StoreStore:写→写顺序
  2. StoreLoad:写→读顺序(最常用且开销最大)
  3. LoadLoad:读→读顺序
  4. LoadStore:读→写顺序

这种细粒度控制允许JVM在不同架构上实现最优性能,只在必要的地方插入必要的屏障。

7. 总结

四种内存屏障共同作用,确保了:

  • volatile写的可见性(StoreStore + StoreLoad)
  • volatile读的 freshness(LoadLoad + LoadStore)
  • 防止不合理的重排序
  • 建立正确的happens-before关系

以下是四种内存屏障的详细对比表格,展示了它们的特点、作用和区别:

屏障类型插入位置保证的操作顺序主要作用典型使用场景开销级别
StoreStorevolatile写操作之前普通写 → volatile写确保volatile写之前的所有普通写操作对其它处理器可见volatile写前的普通变量写入
StoreLoadvolatile写操作之后volatile写 → 后续所有读确保volatile写对所有处理器可见后,才能执行后续的读操作volatile写后可能的读操作
LoadLoadvolatile读操作之前volatile读 → 后续所有读确保先完成volatile读,才能进行后续的读操作volatile读后的普通变量读取
LoadStorevolatile读操作之后volatile读 → 后续所有写确保先完成volatile读,才能进行后续的写操作volatile读后的普通变量写入

详细特性对比

特性StoreStoreStoreLoadLoadLoadLoadStore
防止的重排序类型写-写重排序写-读重排序读-读重排序读-写重排序
保证的可见性使屏障前的写对所有线程可见使屏障前的写对所有线程可见确保读取最新值确保基于最新值进行写入
对应CPU指令通常为no-op(x86)
sfence(某些架构)
mfence(x86)
sync(PowerPC)
lfence(某些架构)通常组合使用
发生频率每次volatile写前每次volatile写后每次volatile读前每次volatile读后
影响范围仅影响写操作顺序影响写后所有读操作仅影响读操作顺序影响读后所有写操作
性能影响较小较大中等中等

实际效果示例对比

屏障类型代码示例 (屏障位置)保证的效果
StoreStorex=1; [SS]; v=2;其他线程看到v=2时,必定能看到x=1
StoreLoadv=1; [SL]; if(x)...执行x的读取时,v=1的写入已经全局可见
LoadLoad[LL]; if(v)...; tmp=x;读取x时,v的读取已经完成且是最新值
LoadStoreif(v)...; [LS]; x=1;写入x=1时,已经基于最新的v值进行了判断

不同处理器架构上的表现

屏障类型x86/64实现ARM实现PowerPC实现
StoreStore通常不需要(隐式保证)dmb ishstlwsync
StoreLoadmfence指令dmb ishsync
LoadLoad通常不需要(隐式保证)dmb ishldlwsync
LoadStore通常不需要(隐式保证)dmb ishlwsync

这个表格总结了四种内存屏障的关键区别,理解这些差异对于编写正确的高性能并发程序非常重要。实际开发中,虽然我们很少直接操作这些屏障(它们由JVM自动插入),但了解其原理有助于诊断并发问题和优化性能。

理解这些内存屏障的工作原理,有助于深入理解Java内存模型和并发编程中的各种可见性、有序性问题。

到此这篇关于Java volatile 内存屏障详解:四种内存屏障的作用与生效机制的文章就介绍到这了,更多相关Java volatile 内存屏障内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 升级springboot3之自动配置导入失效问题及解决

    升级springboot3之自动配置导入失效问题及解决

    这篇文章主要介绍了升级springboot3之自动配置导入失效问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • Mac使用Idea配置传统SSM项目(非maven项目)

    Mac使用Idea配置传统SSM项目(非maven项目)

    本文主要介绍了Mac使用Idea配置传统SSM项目(非maven项目),将展示如何设置项目结构、添加依赖关系等,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • 详解Java中常见语法糖的使用

    详解Java中常见语法糖的使用

    语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,本文主要为大家分享了12个java中常见的语法糖,感兴趣的小伙伴可以了解下
    2023-11-11
  • Spring Boot 集成PageHelper的使用方法

    Spring Boot 集成PageHelper的使用方法

    这篇文章主要介绍了Spring Boot 集成PageHelper的使用方法,文章内容围绕主题展开详细介绍,需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2022-04-04
  • java实现的密码强度检测功能完整示例

    java实现的密码强度检测功能完整示例

    这篇文章主要介绍了java实现的密码强度检测功能,结合完整实例形式分析了java针对密码强度检测相关的字符串遍历、判断,以及输出密码强度等级相关操作技巧,需要的朋友可以参考下
    2019-06-06
  • MyBatis中的配置文件详解

    MyBatis中的配置文件详解

    在 MyBatis 中,配置文件分为 全局配置文件(核心配置文件)和映射配置文件,本文给大家介绍MyBatis中的配置文件相关知识,感兴趣的朋友一起看看吧
    2023-10-10
  • Java数据类型(八种基本数据类型+四种引用类型)以及数据类型转换

    Java数据类型(八种基本数据类型+四种引用类型)以及数据类型转换

    java中除了基本数据类型之外,剩下的都是引用数据类型,下面这篇文章主要给大家介绍了关于Java数据类型(八种基本数据类型 + 四种引用类型)以及数据类型转换的相关资料,需要的朋友可以参考下
    2024-04-04
  • java使用FuncGPT慧函数对Mybatis进行一对一查询映射处理

    java使用FuncGPT慧函数对Mybatis进行一对一查询映射处理

    这篇文章主要介绍了java使用FuncGPT慧函数对Mybatis进行一对一查询映射处理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Spring 配置文件字段注入到List、Map

    Spring 配置文件字段注入到List、Map

    这篇文章主要介绍了Spring 配置文件字段注入到List、Map,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Java中Date和Calendar常用方法

    Java中Date和Calendar常用方法

    这篇文章主要为大家详细介绍了Java中Date和Calendar常用用法,感兴趣的小伙伴们可以参考一下
    2016-09-09

最新评论