什么是内存屏障:
什么是内存屏障
Java中的内存屏障是一种CPU指令, 也是同步机制实现的依赖。内存屏障用于确保多线程环境下的内存可见性和操作的有序性,它是实现线程同步和避免并发问题的关键手段.它可以防止CPU对指令序列进行重排序,从而保证在代码执行过程中,对内存的读写操作按照程序员的意愿来进行
内存屏障通过在代码中插入同步指令来强制线程在特定的位置进行同步,从而解决了多线程环境下的内存可见性和有序性问题。在Java中,常见的内存屏障包括LoadLoad屏障、StoreStore屏障、LoadStore屏障和StoreLoad屏障。这些内存屏障用于限制编译器和处理器在对内存访问进行重排的时机,从而保证程序的正确性。
内存屏障有几种:
- LoadLoad
- LoadStore
- StoreStore
- StoreLoad
什么情况会触发内存屏障:
- volatile关键字:使用volatile关键字修饰的变量在读写操作时会插入相应的内存屏障,保证了对该变量的操作在多线程环境下的可见性和有序性。
- synchronized关键字:进入synchronized代码块或方法会触发一个内存屏障,从而保证进入和退出同步块时的内存可见性和有序性。
- Lock接口:Lock接口的实现类(如ReentrantLock)也会在进入和退出锁定的区域时触发内存屏障,确保线程间的同步和正确的内存访问顺序
当然除了上述场景外 线程的某些方法也会触发内存屏障 比如说Thread.start 和 Thread.join 方法
内存屏障触发会有什么影响:
- 内存可见性:内存屏障可以确保在内存屏障之前的所有写操作都对其他线程可见。这意味着一个线程在内存屏障之前对共享变量的修改会立即对其他线程可见,其他线程可以看到这个修改后的值。
- 指令重排:内存屏障可以阻止编译器和处理器在内存屏障之前和之后对内存访问进行重排。这样可以保证指令执行的顺序符合程序的预期顺序,防止由于重排导致的并发问题。
- 缓存一致性:内存屏障可以确保在内存屏障之前的写操作会刷新到主内存,并在之后的读操作中从主内存加载最新的值。这样可以避免由于处理器缓存数据而导致的线程之间的数据不一致。
volatile如何禁止的指令重排
为了保证volatile变量的禁止指令重排序,Java会在生成的字节码中插入内存屏障(见高亮块下方)来实现
volatile变量的内存屏障是通过一组指令来实现的,包括
- LoadLoad: 确保在读取一个volatile变量前,前面的所有读操作都已完成
- LoadStore: 确保在读取一个volatile变量后,后面的所有写操作都还没有开始
- StoreStore: 确保在写入一个volatile变量前,前面的所有写操作都已经完成
- StoreLoad: 确保在写入一个volatile变量后,后面的所有读操作都还没有开始
当一个线程执行一个读取volatile变量的操作时,Java会插入LoadLoad和LoadStore屏障。LoadLoad屏障会防止该读取操作和前面的任何读操作被重排序,LoadStore屏障则会防止该读取操作和后续的写入操作被重排序
当一个线程执行一个写入volatile变量的操作时,Java会插入StoreStore和StoreLoad屏障。StoreStore屏障会防止该写入操作和前面的任何写入操作被重排序,StoreLoad屏障则会防止该写入操作和后续的读取操作被重排序