volatile解决了什么问题,底层原理是什么
一、解决了什么问题
volatile
是多线程环境下的一个轻量级的同步机制,它主要解决了两个问题:内存可见性 和禁止指令重排序。它不能保证原子性。
内存可见性:
各线程从主内存读取变量到自己的工作内存,线程在自己的工作内存中更新变量,在未来的某个时间点,将修改更新到主内存中。
该过程中,若无 volatile
修饰,则其它线程无法立即看到当前线程的修改。volatile
修饰后,当前线程修改后强制、立即刷到主内存,且基于Happens-Before原则,volatile
修饰的变量的读操作必须在写操作之后,所以其它线程可以立即看到当前线程的修改。
指令重排:
为了优化性能,编译器和处理器可能会在不改变单线程程序语义的前提下,重新排序指令的执行顺序。但在多线程环境下,这种重排序可能导致意想不到的结果
使用 volatile
修饰变量可以禁止 JVM 和处理器对相关的指令进行重排序,保证顺序上的正确性。
二、实现原理
volatile
的语义是通过编译后生成的内存屏障来实现的,内存屏障本质是一类CPU指令。
JMM为volatile
变量的读写提供了不同类型的内存屏障:
- 在每个
volatile
写操作之前,插入一个StoreStore
内存屏障。作用是,确保在该屏障前的所有普通写操作都刷新到主内存之后,才执行volatile
写操作,这样保证了volatile
修改时的内容在各线程间是一致的 - 在每个
volatile
写操作之后,插入一个StoreLoad
内存屏障。作用是,确保volatile
写操作的结果立即对其他线程可见(刷新到主内存),并且会禁止与后续的volatile
读/写操作重排序。注意,该内存屏障的开销相比其它屏障来说,开销更大 - 在每个
volatile
读操作之后,插入LoadLoad
、LoadStore
内存屏障。作用是,确保在执行后续的指令之前,必须先重新从主内存加载volatile
变量的最新值。并且禁止与前面的volatile
读操作重排序
三、和synchronized的区别
volatile | synchronized | |
---|---|---|
本质 | JVM轻量级的关键字 | JVM重量级的关键字 |
原子性 | 不保证原子性 | 保证原子性 |
可见性 | 保证可见性 | 保证可见性 |
有序性 | 通过禁止指令重排保证有序性 | 通过同一时刻只有一个线程运行保证有序性 |
阻塞 | 不会造成线程阻塞 | 会造成线程阻塞 |
适用场景 | 状态标志等 | 复杂的复合操作,需要原子性的场景 |