【< volatile 关键字专题>】

[1] 谈一下你对 volatile 关键字的理解?

**答:**volatile 关键字是用来保证有序性和可见性的。

  1. 保证了不同线程对该变量操作的内存可见性;
  2. 禁止指令重排序。

我们所写的代码,不一定是按照我们自己书写的顺序来执行的,编译器会做重排序,CPU 也会做重排序的,这样做是为了减少流水线阻塞,提高 CPU 的执行效率。这就需要有一定的顺序和规则来保证,不然程序员自己写的代码都不知道对不对了,所以有 happens-before 规则,其中有条就是 volatile 变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作、有序性实现的是通过插入内存屏障来保证的。

解析:

  • volatile 可以保证主内存和工作内存直接产生交互,进行读写操作,保证可见性
  • volatile 仅能保证变量写操作的原子性,不能保证读写操作的原子性。
  • volatile可以禁止指令重排序(通过插入内存屏障),典型案例是在单例模式中使用。

volatile变量的开销:

volatile不会导致线程上下文切换,但是其读取变量的成本较高,因为其每次都需要从高速缓存或者主内存中读取,无法直接从寄存器中读取变量。

[2]Volatile如何保证可见性和有序性?

https://blog.csdn.net/duzhe2905/article/details/106038681?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare

https://blog.csdn.net/qq_35590091/article/details/106986536

image-20200518151004751
1. 可见性

主内存与工作内存

java内存模型规定了所有的变量都存储在主内存。每条线程还有自己的工作内存,线程的工作内存中保存了被改线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量传递均需要通过主内存来完成。当多个线程操作的变量涉及到同一个主内存区域,将可能导致各自的工作线程数据不一致,这样就导致变量同步回主内存的时候可能冲突导致数据丢失。
原文链接:https://blog.csdn.net/y124675160/article/details/78310121

在这里插å¥å›¾ç‰‡æè¿°

volatile修饰的共享变量进行写操作的时候多出一条带lock前缀的指令,lock前缀的指令在多核处理器下会引发两件事情

  1. 将当前处理器缓存行的数据写回到系统内存。
  2. 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存后再进行操作,但是操作完了不知道什么时候写回内存。而对声明了volatile关键字的变量进行写操作,JVM会向处理器发送一条lock前缀的指令,将这个变量所在的缓存行立即写回系统内存。并且为了保证各个处理器的缓存是一致的,实现了缓存一致性协议,各个处理通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,那么下次对这个数据进行操作,就会重新从系统内存中获取最新的值。对应JMM来说就是:

  1. Lock前缀的指令让线程工作内存中的值写回主内存中;
  2. 通过缓存一致性协议,其他线程如果工作内存中存了该共享变量的值,就会失效;
  3. 其他线程会重新从主内存中获取最新的值;

原文链接:https://blog.csdn.net/y124675160/article/details/78310121

2.有序性的

为了性能优化,JVM会在不改变数据依赖性的情况下,允许编译器和处理器对指令序列进行重排序,而有序性问题指的就是程序代码执行的顺序与程序员编写程序的顺序不一致,导致程序结果不正确的问题。而加了volatile修饰的共享变量,则通过内存屏障解决了多线程下有序性问题。

内次屏障分为以下4类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZiQWZ1C-1596597309344)(X:\Users\xu\AppData\Roaming\Typora\typora-user-images\image-20200723232539965.png)]
为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,下面是基于保守策略的JMM内存平展插入策略。

  • 在每个volatile写操作的前面插入一个StoreStore屏障,可以保证前面普通的写操作已经对任意处理器可见。
  • 在每个volatile写操作的后面插入一个StoreLoad屏障,可以保证前面普通的写操作已经对任意处理器可见。
  • 在每个volatile读操作的后面插入一个LoadLoad屏障,确保前面的数据先于后面的指令写入工作内存。
  • 在每个volatile读操作的后面插入一个LoadStore屏障,确保前面的数据先于后面的指令写入工作内存。

volatile在写操作前后插入了内存屏障后生成的指令序列,示意图如下:

在这里插å
¥å›¾ç‰‡æè¿°

volatile在读操作后面插入了内存屏障后生成的指令序列示意图如下:

在这里插å
¥å›¾ç‰‡æè¿°

[3] volatile在什么情况下可以替代锁?

volatile是一个轻量级的锁,适合多个线程共享一个状态变量,锁适合多个线程共享一组状态变量。可以将多个线程共享的一组状态变量合并成一个对象,用一个volatile变量来引用该对象,从而替代锁。

理解volatile和CAS配合使用原理

https://blog.csdn.net/liaoxiaolin520/article/details/93711623

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值