volatile可以保证有序性和可见性。
重排序:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序。不存在数据依赖关系,可以重排序;存在数据依赖关系,禁止重排序。
案例:
(1)图中的第一句和第二句重新排序之后,不影响程序的最终结果。

(2)

volatile内存语义:
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到主内存中读取最新共享变量。
所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。
volatile怎么保证可见性和有序性的?
内存屏障(Memory Barrier)
volatile之内存屏障:
内存屏障(也称内存栅栏,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了Java内存模型中的可见性和有序性(禁重排),但volatile无法保证原子性。
内存屏障之前的所有写操作都要回写主内存,内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)。
内存屏障粗分为两种:
写屏障(Store Memory Barrier):告诉处理器在写屏障之前将所有存储在缓存(store bufferes)中的数据同步到主内存。也就是说当看到Store屏障指令,就必须把该指令之前所有写入指令执行完毕才能继续往下执行。
白话:在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中
读屏障(Load Memory Barrier):处理器在读屏障之后的读操作,都在读屏障之后执行。也就是说在Load屏障指令之后就能保证后面的读取数据指令一定能够读取到最新的数据。
白话:在读指令之前插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据
内存屏障细分为四种:

volatile保证可见性案例:

线程t1中为何看不到被主线程main修改为false的flag的值?
问题可能:
(1):主线程修改了flag之后没有将其刷新到主内存,所以t1线程看不到。
(2):主线程将flag刷新到了主内存,但是t1一直读取的是自己工作内存中flag的值,没有去主内存中更新获取flag最新的值。
我们的诉求:
(1)线程中修改了自己工作内存中的副本之后,立即将其刷新到主内存;
(2)工作内存中每次读取共享变量时,都去主内存中重新获取,然后拷贝到工作内存;
解决:
使用volatile修饰共享变量,就可以达到上面的效果,被volatile修改的变量有以下特点;
(1)线程中读取的时候,每次读取都会去主内存中读取共享变量最新的值,然后将其复制到工作内存
(2)线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存
volatile关键字在Java中用于确保多线程环境下的可见性和禁止指令重排序。内存屏障是实现这一目标的关键,它阻止了处理器和编译器的优化导致的数据不一致。写屏障确保修改后的值立即写回主内存,读屏障则保证从主内存读取最新数据。volatile通过内存屏障确保了线程间共享变量的正确同步。
24万+

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



