valotile面试题

一:valotile只能保证可见性和有序性,不能保证原子性

1:可见性

1):可见性:是在被valotile修饰的变量,被修改后,强制将被修改的变量同步到系统内存中,而其他cpu(即其他线程)在读取自己内部缓存中(也叫工作内存,私有的数据工作栈)的值的时候,发现是被valotile修饰的,会将内部缓存的值设为无效,然后从系统内存中读取。(后面2),3)是帮助理解)

2):举例说明(不可见性):
①:线程A在工作内存中(私有的数据工作栈)更新了变量,还没同步到主内存(系统内存)中,而此时线程B只能获取到还未更新的旧变量值;
②:线程A成功的将变量更新到主内存中,单线程B依旧使用的是自己工作内存中的旧变量值,并没有去主内存中获取新的变量值

解决方案:
对于上面的不可见性,使用valotile修饰的变量会达到的效果是:1. 强制立即同步到主内存中2.强制立即失效工作内存中变量值,这就使得线程B必须去主内存中获取最新的变量值,从而达到线程A的变量修改对线程B是可见的

3):总结:造成数据不可见性的原因是:cpu不是直接和系统内存通信的,而是把变量读取到工作内存中(内部缓存),修改也是在工作内存中,但是 将工作内存的修改同步到系统内存中,是不确定的,有了时间差,从而导致读取到的值,不是最新值

2:有序性

1):有序性:有序性是通过内存屏障来实现的,内存屏障可以理解为在某些指令中插入屏障指令,用以确保,在向屏障指令后面执行的时候,其前面的所有指令已经执行完毕;

2):案例
在写单例模式时,我们通常会采用双层判断的方式,在最内层:
instance = new Singleton()
其实这也有一个隐含的问题:这句赋值语句,其实是分三步来操作的:

a.为instance分配内存

b.调用Singleto构造函数来初始化变量

c.instance指向上一步初始化的对象
  
在jvm做了指令重排序优化后,上述步骤b和c不能保证,可能出现,c先执行,但是对象却没初始化,这时候其他线程判断的时候,发现是非null,但是使用的时候,却没有具体实例,导致报错。

所以,我们可以用valotile来修饰instance,避免该问题。

3):总结:造成指令无序的原因:虚拟机在把代码编译为指令后执行,出于优化的目的,在保证结果不变的情况下,可能会调整指令 执行顺序

3:原子性(不能保证)

最简单的例子就是,i++,在多线程环境下,最终的结果是不确定的,为什么?就是因为这么一个++操作,被编译为指令 后,是多个指令来完成的。那么遇到并发的情况,就会导致彼此“覆盖”的情况
原子操作解释可以参考:

4:注意事项

1):注意:volatile 修饰 数组/Object 时,只有在数组/Object的变量引用发生变化时才内存可见,而数组/Object中的元素/属性被改变时没有内存可见性

例如:volatile int[] a={1,2,3 } ; a[1]=5;//无可见性 a={6,7,8};//有可见性 ;实际上 volatile只是关注变量a的变化,而不关心其引用的数组的变化

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值