[疑难问答]

volatile关键字的作用、原理

[复制链接]
2133|39
手机看帖
扫描二维码
随时随地手机跟帖
geraldbetty|  楼主 | 2024-4-27 18:00 | 显示全部楼层 |阅读模式
在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到“半个”单例。

而发挥神奇作用的volatile,可以当之无愧的被称为Java并发编程中“出现频率最高的关键字”,常用于保持内存可见性和防止指令重排序。

保持内存可见性
内存可见性(Memory Visibility):所有线程都能看到共享内存的最新状态。

失效数据
以下是一个简单的可变整数类:

public class MutableInteger {
        private int value;
        public int get(){
                return value;
        }
        public void set(int value){
                this.value = value;
        }
}
MutableInteger不是线程安全的,因为get和set方法都是在没有同步的情况下进行的。如果线程1调用了set方法,那么正在调用的get的线程2可能会看到更新后的value值,也可能看不到。

解决方法很简单,将value声明为volatile变量:

private volatile int value;
神奇的volatile关键字
神奇的volatile关键字解决了神奇的失效数据问题。

Java变量的读写
Java通过几种原子操作完成工作内存和主内存的交互:

lock:作用于主内存,把变量标识为线程独占状态。
unlock:作用于主内存,解除独占状态。
read:作用主内存,把一个变量的值从主内存传输到线程的工作内存。
load:作用于工作内存,把read操作传过来的变量值放入工作内存的变量副本中。
use:作用工作内存,把工作内存当中的一个变量值传给执行引擎。
assign:作用工作内存,把一个从执行引擎接收到的值赋值给工作内存的变量。
store:作用于工作内存的变量,把工作内存的一个变量的值传送到主内存中。
write:作用于主内存的变量,把store操作传来的变量的值放入主内存的变量中。
volatile如何保持内存可见性
volatile的特殊规则就是:

read、load、use动作必须连续出现。
assign、store、write动作必须连续出现。
所以,使用volatile变量能够保证:

每次读取前必须先从主内存刷新最新的值。
每次写入后必须立即同步回主内存当中。
也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。线程1中对变量v的最新修改,对线程2是可见的。

使用特权

评论回复
pl202| | 2024-5-1 20:34 | 显示全部楼层
volatile关键字可以确保对volatile变量的读写操作在执行顺序上保持一致。这意味着,在程序中,对volatile变量的读写操作不会被编译器重排序。

使用特权

评论回复
chenci2013| | 2024-5-3 18:13 | 显示全部楼层
volatile关键字在单片机编程中非常关键,它帮助程序员管理全局变量、硬件寄存器以及其他可能由系统外部因素更改的变量的状态,确保程序的正确执行。

使用特权

评论回复
caigang13| | 2024-5-3 21:59 | 显示全部楼层
volatile关键字目的是让编译器不过渡优化

使用特权

评论回复
olivem55arlowe| | 2024-5-4 13:04 | 显示全部楼层
volatile关键字的作用是告诉编译器,某个变量可能会被多个不同的线程或者中断服务程序访问并修改

使用特权

评论回复
pentruman| | 2024-5-4 13:11 | 显示全部楼层
volatile关键字在单片机编程中确保了多线程环境下对共享变量的安全访问,并通过内存屏障和缓存一致性协议实现了变量的可见性和禁止编译器优化,这对于保证程序的正确性和稳定性至关重要。

使用特权

评论回复
chenci2013| | 2024-5-4 13:15 | 显示全部楼层
volatile 关键字保证了内存操作的顺序性,因此在多线程或多任务的嵌入式系统中,可以使用 volatile 关键字来确保线程间的同步和通信。例如,一个线程可以修改一个 volatile 变量的值,另一个线程可以读取这个变量的值,从而实现线程间的通信。

使用特权

评论回复
minzisc| | 2024-5-6 12:49 | 显示全部楼层
在单片机编程中,volatile关键字也是一个非常重要的概念

使用特权

评论回复
tabmone| | 2024-5-6 13:12 | 显示全部楼层
对于非 volatile 变量,编译器可能会将其值缓存在寄存器中,以减少对内存的访问。

使用特权

评论回复
phoenixwhite| | 2024-5-6 15:28 | 显示全部楼层
volatile的底层原理依赖于内存屏障和缓存一致性协议。

使用特权

评论回复
lihuami| | 2024-5-6 16:43 | 显示全部楼层
不应该将它的值存储在寄存器中,而是直接在内存中进行读写。这样就能确保每次对该变量的访问都是最新的值

使用特权

评论回复
belindagraham| | 2024-5-6 20:12 | 显示全部楼层
译器在优化代码时,不能假设这个变量的值在两次读取之间不会发生变化,除非它确实看到了该变量被重新赋值。

使用特权

评论回复
robertesth| | 2024-5-6 23:00 | 显示全部楼层
内存屏障会强制让读和写操作都访问主内存,从而实现可见性。

使用特权

评论回复
macpherson| | 2024-5-7 07:58 | 显示全部楼层
当声明一个变量为volatile时,编译器会保证对该变量的所有读/写操作都是直接对内存进行操作,不会将变量的值缓存在寄存器中,也不会对访问该变量的代码进行优化。这就确保了每次读取的都是最新的值。例如,在一个中断服务程序中修改了一个volatile全局变量的值,主程序在下一次访问该变量时能够看到这个改变。

使用特权

评论回复
ingramward| | 2024-5-7 09:17 | 显示全部楼层
在单片机编程中,volatile关键字主要用于确保变量的可见性和防止编译器优化影响代码行为。

使用特权

评论回复
yorkbarney| | 2024-5-7 14:10 | 显示全部楼层
原理上,volatile关键字通过禁止编译器对相关变量进行某些优化,确保每次访问都直接从内存中读取数据,而不是使用缓存或寄存器中的副本。

使用特权

评论回复
nomomy| | 2024-5-7 16:19 | 显示全部楼层
volatile 关键字还可以保证内存操作的顺序性。在某些处理器架构中,编译器可能会重新排列内存操作的顺序以提高性能,但是如果这些内存操作之间存在依赖关系(比如一个写操作之后必须紧跟一个读操作),那么这种重新排列就可能导致问题。使用 volatile 关键字可以告诉编译器不要对这些内存操作进行重新排列。

使用特权

评论回复
maqianqu| | 2024-5-7 17:56 | 显示全部楼层
在中断服务程序或者另一个线程修改了该变量之后,任何后续对该变量的访问都能获取到最新的值。

使用特权

评论回复
rosemoore| | 2024-5-7 18:26 | 显示全部楼层
volatile关键字声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。

使用特权

评论回复
janewood| | 2024-5-7 22:58 | 显示全部楼层
编译器在优化代码时,可能会将某些变量的读取和写入操作进行重排序或优化掉,以提高性能。但是,如果这些变量在程序的控制流之外被改变(例如,由中断服务程序、多线程或其他硬件改变),那么这种优化就可能导致问题。volatile 关键字告诉编译器不要对这些变量进行这种优化。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

1189

帖子

0

粉丝