2024最新Java开发面试大全,99,2024字节跳动Java面试真题解析

本文详细探讨了Java内存模型(JMM)中的内存屏障和volatile关键字的作用。讲解了happens-before规则和as-if-serial语义,以及如何通过插入内存屏障防止处理器和编译器的重排序。此外,还分析了volatile的内存语义,包括其对可见性和有序性的保证,以及在实际例子中如何体现。最后,强调了volatile无法保证原子性,指出在需要原子性保障的情况下应使用锁或其他并发控制机制。
摘要由CSDN通过智能技术生成

其中2和3属于CPU执行阶段的重排序,1属于编译器阶段的重排序。编译器会遵守happens-before规则和as-if-serial语义的前提下进行指令重排。

happens-before规则:如果A happens-before B,且B happens-before C,则需要保证A happens-before C。

as-if-serial语义:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器、Runtime和处理器都必须遵守as-if-serial语义。

对于处理器重排序,JMM要求Java编译器在生成指令序列时,插入特定类型的内存屏障指令,来禁止特定类型的处理重排序。

JMM的内存屏障

========

上面了解了CPU的内存屏障分类,在JMM中把内存屏障分为四类:

  • LoadLoad Barriers:示例,Load1;LoadLoad;Load2,确保Load1数据的装载先于Load2及所有后续指令的装载;

  • StoreStore Barriers:示例,Store1;StoreStore;Store2,确保Store1数据对其他处理器可见(刷新到内存)先于Store2及所有后续存储指令的存储;

  • LoadStore Barriers:示例,Load1;LoadStore;Store2,确保Load1数据装载先于Store2及所有后续存储指令刷新到内存;

  • StoreLoad Barriers:示例,Store1;StoreLoad;Load2,确保Store1数据对其他处理器变得可见(刷新到内存)先于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

其中,StoreLoad Barriers同时具有前3个的屏障的效果,但性能开销很大。

为了实现volatile内存语义,JMM会分别限制这两种类型的重排序类型。下图是JMM针对编译器制定的volatile重排序规则表。

99%的人没弄懂volatile的设计原理,更别说灵活运用了

JMM重排序

从图中可以得出一个基本规则:

  • 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后。

  • 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。

  • 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎不可能。为此,JMM采取保守策略。下面是基于保守策略的JMM内存屏障插入策略:

  • 在每个volatile写操作的前面插入一个StoreStore屏

  • 29
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值