volatile 是轻量级的 synchronized,一般作用于变量。相比于synchronized关键字,volatile关键字的执行成本更低,因为它不会引起线程上下文的切换和调度。
volatile主要有以下两个功能:
- 保证共享变量的内存可见性(即当一个线程修改一个共享变量时,另一个线程能读到这个修改的值)。
- 禁止指令重排序
volatile 的用途:
从volatile的内存语义上来看,volatile可以保证内存可见性且禁止重排序。
在保证内存可见性这一点上,volatile有着与锁相同的内存语义,所以可以作为一个“轻量级”的锁来使用。但由于volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁可以保证整个临界区代码的执行具有原子性。所以在功能上,锁比volatile更强大;在性能上,volatile更有优势。
volatile的特性
volatile可以保证可见性和有序性。
-
可见性:
volatile可以保证不同线程对共享变量进行操作时的可见性。即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。 -
有序性:
volatile会通过禁止指令重排序进而保证有序性。 -
原子性:对于单个的
volatile修饰的变量的读/写是可以保证原子性的,但对于v++这种复合操作并不能保证原子性。所以说volatile不具备原子性。为什么说 volatile 不保证原子性?
Java中只有对基本类型变量的赋值和读取是原子操作,如 i = 1的赋值操作,但是像 j = i 或者 i++ 这样的操作都不是原子操作,因为他们都进行了多次原子操作,比如先读取 i 的值,再将 i 的值赋值给 j,两个原子操作加起来就不是原子操作了。
比如 i++ ,i = i + 1 分为三个操作
- 读取 i 的值
- 自增 i 的值
- 把 i 的值写回内存
这个过程中可能线程A读取了 i 的值,然后线程切换,即使是被volatile修饰,主存中变量的值也还没变化,所以另一个线程B也读取了 i 未被修改的值,之后线程切换回A,进行 + 1 操作,写回内存;而线程B写回内存会把线程A修改的值覆盖。
所以即便是volatile具有可见性,也不能保证对它修饰的变量具有原子性。
可以通过 synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。
volatile实现内存可见性原理
volatile可以保证内存可见性的关键是volatile的读/写实现了缓存一致性。
缓存一致性协议 MESI 的主要内容为:
多个CPU从主内存读取同一个数据到各自的高速缓存,当其中某个CPU修改了缓存里的数据,该数据会马上同步会主内存,其他CPU通过总线嗅探机制可以感知到数据的变化从而将自己缓存里

volatile关键字在Java多线程中用于保证共享变量的可见性和有序性,防止指令重排序。它通过内存屏障确保缓存一致性,并在多核处理器下避免数据不一致。然而,volatile并不保证原子性,例如i++这类操作在多线程环境中仍可能出现问题。双重检查锁使用volatile防止对象半初始化,确保线程安全。
最低0.47元/天 解锁文章
24万+

被折叠的 条评论
为什么被折叠?



