volatile的应用及实现

本文详细探讨了Java中volatile变量的特性和应用,解释了其如何确保多线程环境下共享变量的可见性,以及它是如何避免线程上下文切换和调度的。通过汇编代码示例,展示了volatile变量在处理器层面的实现机制,包括缓存一致性协议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile的应用

volatile是轻量级的synchronized,它在多处理开发中保证了共享变量的“可见性”,可见性的意思是当一个线程修改共享变量时,另一个线程能读到这个修改的值。他不会引起线程上下文切换和调度。

volatile的实现原理

例:instance = new Singleton(); //instance是volatile变量。

转换成汇编代码,如下:

0x01a3de1d: mov b $0---------;0x01a3de24: lock------

我们不关注汇编代码具体是什么,关键在于其中的lock,有volatile变量修饰共享变量进行写操作时会多出第二行代码,其中lock前缀的指令在多核处理器引发了两件事情:

  1. 将当前处理器缓存行的数据写回到系统内存。
  2. 这个写回内存操作会使在其他CPU里缓存了改地址的数据无效。

为了提高处理速度,处理器会先将内存中的数据读到内部缓存后再进行操作,但操作完之后不知何时会写到内存。如果对生命了volatile的变量进行写操作,JVM会发送一条Lock指令,将这个变量所在的缓存行的数据写回系统内存。而且对于其他处理器其中还会有缓存一致性协议:每个处理器通过嗅探在总线上的数据来检查自己缓存的值是不是过期了,当发现自己缓存行对应的内存地址被修改,会将自己缓存行设置为无效转态,当处理器对这个数据进行修改操作的时候,会重新从内存中把数据读到缓存中来。

以上来自《Java并发编程的艺术》

### Java `volatile` 关键字的内存屏障实现原理 #### 背景介绍 在多线程环境中,程序运行的结果可能受到缓存一致性的影响。由于现代处理器架构中引入了指令重排序优化以及高速缓存机制,可能导致某些变量的可见性和有序性出现问题。为此,Java 提供了 `volatile` 关键字来解决这些问题。 `volatile` 的核心作用在于确保变量的可见性和防止指令重排序[^1]。具体来说: - **可见性**:当一个线程修改了一个 `volatile` 变量的值时,新值能够立即同步到主内存中,而其他线程可以及时看到最新的值。 - **有序性**:通过插入特定类型的内存屏障(Memory Barrier),阻止编译器和 CPU 对指令进行乱序执行。 --- #### 内存屏障的作用 内存屏障是一种硬件级别的指令,用于控制数据加载和存储的操作顺序。它分为多种类型,在 JVM 中主要涉及以下两种屏障: 1. **LoadLoad 屏障** - 确保前面的读操作完成后才能继续后面的读操作。 2. **StoreStore 屏障** - 确保前面的写操作完成后才能继续后面的写操作。 3. **LoadStore 屏障** - 阻止读操作越过后续的写操作。 4. **StoreLoad 屏障** - 是最昂贵的一种屏障,因为它既阻止之前的写操作被重排到后面,也阻止之后的读操作提前发生。 对于 `volatile` 来说,其行为可以通过 StoreLoad 屏障来描述[^4]。这意味着每次对 `volatile` 变量的读写都会触发一次完整的内存屏障序列,从而保障线程间的正确通信。 --- #### 源码层面分析 通过对 OpenJDK 的源代码深入研究可知,`volatile` 的底层实现依赖于 Unsafe 类中的方法调用。例如,在 unsafe.cpp 文件里可以看到类似这样的逻辑片段: ```cpp void OrderAccess::store_fence(volatile jint* dest, jint value) { // Insert a memory barrier before the store operation. __asm__ volatile ("lock; addl $0, (%%esp)" : : : "memory"); } ``` 上述代码展示了如何利用汇编语言向操作系统发出信号以强制刷新本地缓存并通知其他核更新状态。这里的 `"lock"` 前缀实际上就是一种轻量级互斥锁形式,它可以有效避免竞态条件的发生。 --- #### 示例解析 考虑一段简单的例子说明 `volatile` 如何影响实际性能表现: ```java public class VolatileExample { private static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { new Thread(() -> { System.out.println("Thread started."); while (!flag); System.out.println("Loop exited."); }).start(); Thread.sleep(100); // Simulate delay to let thread start first. flag = true; System.out.println("Main set flag=true."); } } ``` 在这个场景下,如果没有声明 `flag` 为 `volatile` ,那么子线程可能会因为一直使用自己副本里的旧值而导致死循环现象出现[^3] 。然而一旦加上此限定符,则可保证任何时刻都能获取最新版本的数据拷贝。 --- #### 性能权衡 尽管 `volatile` 提高了跨进程间协作的安全系数,但它同时也带来了额外开销。这是因为频繁地跨越 L1/L2 缓存甚至直达 DRAM 存储单元必然消耗更多时间资源[^2]。因此开发者应当谨慎评估是否真的有必要应用此类修饰符而非简单依靠 synchronized 锁定整个区域范围内的独占权限管理策略。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值