volatile关键字可见性问题

Volatile关键字

保证共享变量可见性
多个线程,读取和写,读取线程不能及时读取到改变的值
实现跨线程可见性,使用volatile关键字
指令lock,在修改变量属性时候加lock,如果不加volatile关键字,这个lock指令是不存在的,这就是区别,多个汇编指令lock,

可见性到底是什么?

硬件层面
最核心组件,cpu,内存,磁盘,速度差异,
绝大部分程序计算都有依靠,
最大化cpu资源
1.cpu增加高速缓存
2.引入线程,进程
3.指令优化,指令重排序
主内存和cpu之间加入cpu高速缓存,cpu高速缓存分为L1,L2,L3
L1,L2是cpu私有。L1分为数据缓存和指令缓存
L3缓存是多个cpu共享的
数据同步到主内存,同步之前其他cpu拿到的是旧值,缓存一致性问题导致的,高速缓存会带来缓存一致性问题,cpu层面会解决,通过两种方案,缓存锁,总线锁。
如何实现缓存锁,基于缓存一致性协议(MESI),达到缓存数据一致

但是没有解决可见性,MESI协议会带来问题,重排序带来可见性问题,硬件方面无法完全解决可见性问题。
CPU提供内存屏障指令,在程序中可以加入内存屏障,从而达到可见性

CPU三种屏障,写屏障,读屏障,全屏障。

volatile里面lock(缓存锁)指令相当于内存屏障指令

jmm层面

可见性根本原因,高速缓存,和重排序,所以还是解决重排序问题
volatile关键字可以解决这个问题
语言级别抽象内存模型

jmm解决有序性和可见性
jmm通过内存屏障禁止重排序

分为主内存和工作内存
如何解决可见性和有序问题
volatile,synchronized,final,happens-before
顺序的不一致就会产生可见性问题

Happens-before规则

volatile规则
A happens-before B一个结果对另一个结果可见,

jmm哪些操作会建立happens-before原则
程序的顺序规则

volatile

是Java虚拟机提供的轻量级的同步机制

面试问三大特性:

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排序(内存屏障)

说到volatile就想到JMM

JMM -----Java内存模型

JMM三大特性
可见性
原子性
有序性

java内存模型,和volatile有啥关系
JMM是一种抽象的概念,并不真实存在,它描述的是一种规则或规范,通过这组规范定义了程序中各个变量的访问方式

JMM关于同步的规定
1.线程解锁前,必须把共享变量的值刷新会主内存
2.线程加锁前,必须读取主内存的最新值到自己的工作内存
3.加锁解锁是同一把锁

硬盘弱于内存弱于cpu,现在要求数据快速响应,内存读取数据快于硬盘

可见性

主内存就是电脑内存(比方内存条就是),硬件
多个线程去修改一个值,每个线程会把主内存中的值拷贝到自己的工作内存中,各自的工作内存中有一份自己的拷贝数据,然后线程修改自己的工作内存,然后将自己的值写回主内存,但是其他线程不知道现在的值已经修改,所以我们要有一个机制,就是修改主内存值后要及时通知其他线程,这种及时通知其他线程的操作就是JMM内存模型里面的一个特性,可见性。(线程间的通信必须通过主内存来完成,工作内存是独立的)
这就是JMM内存模型的一大特性,可见性。

开发时通过volatile去解决可见性问题。(卖票)

主存修改,其他立马可见。

不保证原子性

volatile为什么是轻量级,因为不保证原子性,保证有序(禁止指令重排序),synchonized保证原子性

不保证原子性
什么是原子性,即不可分割,要么同时成功,要么同时失败
++操作底层汇编字节码是三步操作,三个指令,先读,然后++,然后写回主内存
在这里插入图片描述

所以volatile不保证原子性

volatile不保证原子性,如何解决

JMM内存模型要求保证原子性的,如何解决原子性
第一种加synchonized(不用)
第二种atomic(底层cas)

这里又引出一个cas

CAS

什么是CAS?比较并交换,CompareAndSet,先比较,后交换
(期望值,更新值)
主内存中的值,希望主内存的值和期望值一样,和主内存的值对比,如果true,说明没人动过,和期望值一样,就可以修改为更新值,感觉有点像记录版本号,每次比较版本号。mysql

CAS底层原理

自旋锁和unsafe类

cas是unsafe类中方法,unsafe类在jdk的jar包下面有个rt.jar里面,sun.misc包中,这个类大部分是native修饰,所以可以直接调用操作系统底层资源执行相应任务

unsafe类根据内存偏移地址valueoffset,获取数据的。
在这里插入图片描述

unsafe类是cas的核心类,由于java无法直接访问底层系统,需要通过本地native方法来访问,unsafe相当于一个后门,基于该类可以直接操作特定内存的数据,Unsafe类在sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为java中cas操作的执行依赖于Unsafe类的方法。

unsafe类底层源码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自旋锁

这个也是AtomicInteger为什么用cas,不用synchonized的原因,synchonized加锁并发下降,而cas通过自旋锁可以反复比较,知道成功,既保证一致性,又提高并发。

cas如何保证原子性

cas如何保证原子性,靠的就是底层的Unsafe类,变量中的valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。
调用unsafe类中的cas方法,jvm会帮我们实现出cas汇编指令,这是一种完全依赖于硬件的功能,通过它实现原子性,
cas是一条cpu的原子指令,天生就是连续的,不会造成所谓的数据不一致问题

CAS缺点

  1. 这里源码中用到了do while自旋锁,所以这也是cas的缺点,高并发下循环时间可能太长,会加大cpu开销。如果cas失败,一直尝试,高并发下面有问题。
    代码层面破环循环,设置合适的循环次数。
  2. 只能保证一个共享变量的原子性。this对象。对于多个共享变量操作,无法保证原子性,这时候可以加锁保证原子性。 AtomicReference可以保证多个变量,创建一个对象,对象里面操作多个变量
  3. cas的ABA问题。解决,时间戳原子引用,

CAS的ABA问题

ABA问题,狸猫换太子,一个线程a改为b又改为a,另一个线程看不出修改了,其实是已经修改过了,只不过又改回去了,中间的过程是有问题的。AtomicStampedReference

如何解决ABA问题

如果不介意aba问题,那无所谓,否则就得解决。

通过时间戳原子引用 AtomicStampedReference。版本号原子引用。修改版本号,类似时间戳。解决ABA问题。

有序性:禁止指令重排序

计算机执行为了提高性能,编译器和处理器会对指令进行重排序
源代码 ——> 编译器优化的重排 ——> 指令并行的重排 ——>内存系统的重排 ——> 最终执行的指令
这样就有问题了,顺序打乱,所以我们要禁止计算机的指令重排序,volatile可以禁止指令重排序。

volatile是如何解决指令重排序的?这里引出禁止指令重排解决方式------内存屏障

内存屏障(内存栅栏)

是一个CPU指令,作用有两个
1.保证特定操作的执行顺序
2.保证某些变量的内存可见性
插入内存屏障禁止在内存屏障前后的指令执行重排序优化
内存屏障的另外一个作用就是强制刷出各种cpu的缓存数据,因此任何cpu上的线程都能读取到这些数据的最新版本。

JMM内存屏障
语言级别内存屏障,
ACC_VOLAITLE这个判断是否加了volatile关键字
被volatile修饰后,都会有个storeload操作,加屏障。解决可见性问题
在这里插入图片描述
定义访问规则,四个屏障类型

volatile通过加内存屏障来实现禁止指令重排序。JMM为volatile加内存屏障有以下4种情况:

在每个volatile写操作的前面插入一个StoreStore屏障,防止写volatile与后面的写操作重排序。

在每个volatile写操作的后面插入一个StoreLoad屏障,防止写volatile与后面的读操作重排序。

在每个volatile读操作的后面插入一个LoadLoad屏障,防止读volatile与后面的读操作重排序。

在每个volatile读操作的后面插入一个LoadStore屏障,防止读volatile与后面的写操作重排序。

在这里插入图片描述

对volatile变量进行写操作时,会在写操作后加入一条store屏障指令,将工作内存中的共享变量值刷新回到主内存。
在这里插入图片描述

对volatile变量进行读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量
在这里插入图片描述

你在哪些地方使用过volatile?

volatile经典使用场景
单例模式,多线程下使用,加synchonized可以解决,但是并发下降
加入volatile,禁止指令重排,双端检索机制。
在这里插入图片描述
这种解决了线程安全问题。多线程访问也是创建出一个。
如何造成安全问题?new对象分为几步,指令重排序导致问题
。所以要用volatile关键字,不要指令重排

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值