面试准备-5

volatile实现什么能力,怎么实现的?

1. volatile能保证变量在多线程之间的可见性。
2. volatile禁止CPU执行时进行指令重排操作(内存屏障)从而能保证有序执性。
3. volatile不能保证原子性

补充

内存可见性问题:

在多线程共享一个数据块下,一个线程对数据进行修改操作时,其它线程是无法感知的。甚至会被编译器优化到完全不可见的程度。
 例如:当两个线程同时操作一个内存,例如一个读一个写,但是当“写线程”进行的修改的时候,“读线程”可能读到修改前的数据,也可能读到修改后的数据,这是不确定的。


不可见的原因:


 CPU为了提高数据获取速率,会设置缓存。
 在多核CPU下,每个核都有自己的独占缓存进行数据存取,只有在所有处理结束后,才会将数据同步到主存中。
 所以会导致有些核读取到的是过期的数据。

volatile的原理

简单来说:当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。
当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。
当一个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。

内部如何做到缓存主存同步的?


 缓存一致性协议(MESI):当CPU写数据时,如果发现操作的变量时共享变量,即其他线程的工作内存也存在该变量,于是会发信号通知其他CPU该变量的内存地址无效。当其他线程需要使用这个变量时,如内存地址失效,那么它们会在主存中重新读取该值。

volatile禁止代码重排序 

指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果时正确的,但是无法保证程序的操作顺序与代码顺序一致。


 这在单线程中不会构成问题,但是在多线程中就会出现问题。
 非常经典的例子是在单例方法中同时对字段加入voliate,就是为了防止指令重排序。

对象实例化的步骤

memory = allocate(); // 1.分配对象内存空间
instance(memory); // 2.初始化对象
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance!=null

代码重排序后的步骤

memory=allocate(); // 1.分配对象内存空间
instance=memory; // 3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
instance(memory); // 2.初始化对象

volatile的原理

volatile禁止重排序是利用内存屏障,保证有序性。
内存屏障是一组CPU指令,用于实现对内存操作的顺序限制。
Java编译器在生成指令系列时,在适当的位置会插入内存屏障来禁止处理器对指令的重新排序。

(1)volatile会在变量写操作的前后加入两个内存屏障,来保证前面的写指令和后面的读指令是有序的。

(2)volatile在变量的读操作后面插入两个指令,禁止后面的读指令和写指令重排序。

volatile不能保证原子性,要想保证原子性我们要使用锁机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值