Volatite

volatite特性:可见性,有序性

1.可见性

当多个线程同时访问一个变量,其中一个线程对该变量进行了修改,其他线能立刻看到被修改的值。

volatite是如何实现可见性的?
如果一个变量被volatile所修饰的话,在每次数据变化之后,其值都会被强制刷入主存。而其他处理器的缓存由于遵守了缓存一致性协议,当再次访问这个变量时会从主存加载到自己的缓存中。

缓存一致性协议 (MESI协议)
当cpu操作数据时,发现操作的变量是共享变量,即在其他cpu中也存在该变量的副本,会发出信号到总线,其他cpu通过嗅探机制,监听到共享数据有修改,则会将自己工作线程缓存空间内该变量的缓存行设置为无效态,当其他cpu读取这个变量时,发现自己缓存中缓存该变量的缓存行无效,就会从主存重新读取。

嗅探机制
每个处理器通过监听在总线上传播的数据来检查自己的缓存值是不是过期了,如果处理器发现自己缓存行对应的内存地址修改,就会将当前处理器的缓存行设置无效状态,当处理器对这个数据进行修改操作的时候,会重新从主内存中把数据读到处理器缓存中。

2.有序性

即程序执行的顺序按代码的先后顺序执行。

volatite关键字修饰的共享变量在编译时,会在指令序列中插入内存屏障达到禁止重排序的效果,从而实现代码的有序性。

指令重排序?
编译器和处理器为了优化程序性能和运行效率对指令进行重新排序。
例如:

a

重排序虽然可以提高执行的效率,但是在并发执行下,JVM虚拟机底层并不能保证重排序下带来的安全性等问题.

重排序规则:
1.不会对存在数据依赖关系的操作进行重排序。
2.单线程下程序的执行结果不能被改变。

内存屏障指令:读屏障(Load Barrier) 和 写屏障(Store Barrier)

内存屏障类型:
LoadLoad:语句Load1; LoadLoad; Load2,Load1的读取操作在Load2及后续读取操作之前执行。
StoreStore:语句Store1; StoreStore; Store2,在Store2及后续写入操作之前,要确保Store1写入操作已刷新到主内存。
LoadStore:语句Load1; StoreStore; Store2,在Store2及后续写入操作之前,要确保Load1读取操作已结束。
StoreLoad:语句Store1; StoreStore; Load2,保证Store1的写操作已刷新到主内存之后,Load2及其后的读操作才能执行。

内存屏障插入规则:
每个volatile 写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障
每个volatile 读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障

volatite禁止指令重排序的规则:
1.第一个操作是 volatile 读,不管第二个操作是什么,都不能重排序。
2.第二个操作是 volatile 写,不管第一个操作是什么,都不能重排序。
3.第一个操作是 volatile 写,第二个操作是 volatile 读,不能重排序。

volatile 不保证原子性

b.png

在多线程并发的情况下,可能在现在写了数据还没有同步到主内存的时候,其他线程同样进行了操作,比如count++时候,初始时时1,一个线程两个线程同时读取到了1,然后都进行了+1的操作,这样的话数据只会变成了2,并不会变成3,导致了不能保证原子性。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容