【Java并发编程】之volatile关键字

volatile具有可见性和有序性,一条线程修改了一个变量的值,新值对于其他线程来说是立即可知的。不具有原子性。

说到volatile,必须要先将JMM以及重排序。

JMM内存模型

为了解决内存和CPU速度不一致,提高效率。在JMM中,每个线程有其自己私有的工作内存。共享变量会先放在主存中,每个线程都有属于自己的工作内存,并且会把位于主存中的共享变量拷贝到自己的工作内存,之后的读写操作均使用位于工作内存的变量副本,并在某个时刻将工作内存的变量副本写回到主存中去。

什么是共享变量?

在java程序中所有实例域,静态域和数组元素(所有线程均可访问到,是可以共享的),而局部变量是线程私有的,不需要共享。

JMMåå­æ¨¡åçæ½è±¡ç»æ示æå¾

由于线程对变量的修改修改的是工作内存中的副本,因此别的线程是不知道的,这样就会导致数据不一致的问题

重排序

为了提高性能,编译器和处理器实际上会对指令进行重排序。

如果两个操作没有数据依赖性,那么这两个操作就有可能发生重排序。

数据依赖性:

int a=1;
int b=2;
b=3;
a=b;

 

volatile实现原理

使用volatile可以解决这两个问题,保证可见性和有序性。

保证可见性

在生成汇编代码时会在volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令。

如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。

保证有序性

volatile会在内存中和插入一个内存屏障,来禁止指令重排序。

但是,volatile不具有原子性。

使用场景

对变量的写操作不依赖于当前值

该变量没有包含在具有其他变量的不变式中

事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。

模式 #1:状态标志

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

模式 #2:一次性安全发布

如果不用volatile,则因为内存模型允许所谓的“无序写入”,可能导致失败。——某个线程可能会获得一个未完全初始化的实例。

DCL案例

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

模式 #4:“volatile bean” 模式

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

参考:

https://blog.csdn.net/vking_wang/article/details/9982709#     volatile使用场景

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值