volatile的适用场景

本文探讨了Volatile变量在Java中的使用条件及其适用场景,包括状态标志、一次性安全发布等模式。此外还讨论了Volatile变量如何在特定情况下替代锁以提高性能。

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

volatile的使用条件

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子性。这就是说线程能够自动发现 volatile 变量的最新值。

Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。

使用条件

您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

  • 对变量的写操作不依赖于当前值。
  • 该变量没有包含在具有其他变量的不变式中。

实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。

volatile的适用场景

模式 #1:状态标志

也许实现 volatile 变量的规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。

模式 #2:一次性安全发布(one-time safe publication)

在缺乏同步的情况下,可能会遇到某个对象引用的更新值(由另一个线程写入)和该对象状态的旧值同时存在。

这就是造成著名的双重检查锁定(double-checked-locking)问题的根源,其中对象引用在没有同步的情况下进行读操作,产生的问题是您可能会看到一个更新的引用,但是仍然会通过该引用看到不完全构造的对象。双重检查的单例模式。

模式 #3:独立观察(independent observation)

安全使用 volatile 的另一种简单模式是:定期 “发布” 观察结果供程序内部使用。【例如】假设有一种环境传感器能够感觉环境温度。一个后台线程可能会每隔几秒读取一次该传感器,并更新包含当前文档的 volatile 变量。然后,其他线程可以读取这个变量,从而随时能够看到最新的温度值;换句话说就是只读的操作或者变量。

模式 #4:“volatile bean” 模式

volatile bean 模式的基本原理是:很多框架为易变数据的持有者(例如 HttpSession)提供了容器,但是放入这些容器中的对象必须是线程安全的。

在 volatile bean 模式中,JavaBean 的所有数据成员都是 volatile 类型的,并且 getter 和 setter 方法必须非常普通——即不包含约束!

模式 #5:开销较低的“读-写锁”策略

如果读操作远远超过写操作,您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。

如下显示的线程安全的计数器,使用 synchronized 确保增量操作是原子的,并使用 volatile 保证当前结果的可见性。如果更新不频繁的话,该方法可实现更好的性能,因为读路径的开销仅仅涉及 volatile 读操作,这通常要优于一个无竞争的锁获取的开销。


http://blog.youkuaiyun.com/vking_wang/article/details/9982709

### Volatile 关键字在 Java 和 C++ 中的应用场景 #### Java 中的 `volatile` 关键字应用场景及作用 在 Java 中,`volatile` 主要用于确保多个线程能够看到共享变量的最新值。当一个字段被声明为 `volatile` 后,在每次访问该字段时都会强制从公共主存中读取最新的值而不是缓存中的副本;同样地,在更新这个字段的时候也会立即同步回主存中去[^2]。 例如: ```java public class Example { private volatile boolean flag = false; public void setFlag(boolean value) { this.flag = value; } public boolean getFlag() { return this.flag; } } ``` 在这个例子中,如果两个线程分别调用了 `setFlag()` 方法设置标志位以及不断轮询 `getFlag()` 来判断状态变化,则可以保证其中一个线程修改后的结果能及时反映给另一个正在查询此标记的线程。 此外,JVM 还会在读写操作前后插入内存屏障以保障指令执行顺序不会因为编译器优化而被打乱,并且使得这些操作对于其他处理器来说也是有序可见的。 #### C++ 中的 `volatile` 关键字应用场景及作用 C++ 的 `volatile` 修饰符主要用于告诉编译器不要对该对象进行任何假设性的优化处理,即防止编译器对涉及此类数据的操作做重排序或删除冗余读/写等优化措施。这通常适用于硬件寄存器映射、信号量机制或是多线程编程里需要直接控制特定存储位置的情况[^1]。 下面是一个简单的实例展示了如何利用 `volatile` 避免死循环条件下的无限等待问题: ```cpp #include <iostream> #include <thread> volatile bool flag = false; void threadFunc() { while (!flag) {} // 此处如果没有 volatile, 编译器可能会认为 flag 不会被改变从而将其优化成常数表达式 } int main(){ std::thread t(threadFunc); // 模拟一段耗时操作 std::this_thread::sleep_for(std::chrono::seconds(1)); flag = true; t.join(); return 0; } ``` 这里通过将全局布尔型变量 `flag` 定义为 `volatile`, 可以阻止编译器对其进行不必要的优化,进而确保即使是在单核 CPU 上也能正确工作——即主线程最终会成功唤醒子线程并结束其运行。 综上所述,虽然两者都叫作 `volatile` ,但在具体应用场合和语义含义上有明显区别:前者侧重于解决跨线程间的数据一致性问题;后者则更多关注底层硬件交互过程中的不可预测行为预防。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值