java内存屏障+可见性

1.内存屏障:(如果字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令)

(1)内存屏障(memory barrier)是一个CPU指令。这条指令可以确保一些特定指令的执行顺序,影响一些数据的可见性(可能是某些指令执行后的结果)。

(2)插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存

例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而
不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。

//现代的操作系统都是多处理器.而每一个处理器都有自己的缓存,并且这些缓存并不是实时都与内存发生信息交换.这样就可能出现一个cpu上的缓存数据与另一个cpu上的缓存数据不一致的问题.而这样在多线程开发中,就有可能导致出现一些异常行为。

2.常见的应用场景

(1)通过 Synchronized关键字包住的代码区域,当线程进入到该区域读取变量信息时,保证读到的是最新的值.这是因为在同步区内对变量的写入操作,在离开同步区时就将当前线程内的数据刷新到内存中,而对数据的读取也不能从缓存读取,只能从内存中读取,保证了数据的读有效性.这就是插入了StoreStore屏障;
(2)使用了volatile修饰变量,则对变量的写操作,会插入StoreLoad屏障;
(3)其余的操作,则需要通过Unsafe这个类来执行;

3.操作系统底层提供了4种屏障?

(1)LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
(2)StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
(3)LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
(4)StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。


 

### Java 内存屏障原理及在多线程中的应用 Java 内存模型(Java Memory Model, JMM)定义了多线程程序中内存与变量访问的规范。内存屏障是 JMM 的核心机制之一,用于控制指令重排序并确保内存操作的可见性内存屏障通过插入特定的指令阻止编译器和 CPU 对指令进行不必要的优化,从而保证并发程序的正确执行 [^3]。 #### 内存屏障的主要类型 1. **LoadLoad 屏障** LoadLoad 屏障确保所有后续的加载操作必须在该屏障之前的加载操作完成后才能执行。这种屏障通常用于读取 volatile 变量时,在读操作之后插入 LoadLoad 屏障以确保后续读操作不会被提前执行 [^2]。 2. **StoreStore 屏障** StoreStore 屏障确保所有后续的存储操作必须在该屏障之前的存储操作完成后才能执行。这种屏障通常用于写入 volatile 变量时,在写操作之前插入 StoreStore 屏障以确保前面的写操作不会被延迟执行 [^2]。 3. **LoadStore 屏障** LoadStore 屏障确保所有后续的存储操作必须在该屏障之前的加载操作完成后才能执行。这种屏障防止了读操作和写操作之间的重排序,常用于 volatile 写操作前的处理 [^1]。 4. **StoreLoad 屏障** StoreLoad 屏障是最强类型的屏障,它确保所有后续的加载操作必须在该屏障之前的存储操作完成后才能执行,同时所有后续的存储操作也必须在该屏障之前的加载操作完成后才能执行。这种屏障可以防止所有可能的重排序,并且通常用于 volatile 读写操作之间 [^2]。 #### Java 中的内存屏障应用场景 1. **Synchronized 锁** 在使用 synchronized 关键字实现同步锁时,JVM 会自动插入内存屏障以确保锁的可见性和有序性。进入 synchronized 块前会插入 LoadLoad 和 LoadStore 屏障,退出 synchronized 块后会插入 StoreStore 和 StoreLoad 屏障 [^2]。 2. **volatile 关键字** 使用 volatile 关键字修饰的变量具有可见性和禁止指令重排序的特性。写 volatile 变量时会在写操作前插入 StoreStore 屏障,在写操作后插入 StoreLoad 屏障;读 volatile 变量时会在读操作后插入 LoadLoad 屏障和 LoadStore 屏障 [^2]。 3. **final 关键字** final 关键字用于确保对象构造完成后其字段的值对其他线程可见。在 final 字段的初始化过程中,JVM 会插入适当的内存屏障以防止构造过程中的重排序 。 #### 示例代码:volatile 关键字的内存屏障作用 ```java public class VolatileExample { private volatile boolean flag = false; public void toggleFlag() { flag = !flag; // 写操作,插入 StoreStore 和 StoreLoad 屏障 } public boolean getFlag() { return flag; // 读操作,插入 LoadLoad 和 LoadStore 屏障 } } ``` 上述代码展示了 volatile 关键字如何通过内存屏障确保多线程环境下的可见性和有序性。当 `toggleFlag` 方法被调用时,`flag` 的更新将立即对其他线程可见,并且不会与其他内存操作发生重排序 [^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值