1.问:请你说下对Valotile的了解,以及使用场景。
分析:多线程编程,我们要解决的问题主要集中在三个方面:
a. 原子性,最简单的例子就是,i++,在多线程环境下,最终的结果是不确定的,为什么?就是因为这么一个++操作,被编译为指令后,是多个指令来完成的。那么遇到并发的情况,就会导致彼此“覆盖”的情况。
b. 可见性,通俗解释就是,在A线程对一个变量做了修改,在B线程中,能正确的读取到修改后的结果。究其原理,是cpu不是直接和系统内存通信,而是把变量读取到L1,L2等内部的缓存中,也叫做私有的数据工作栈。修改也是在内部缓存中,但是何时同步到系统内存是不能确定的,有了这个时间差,在并发的时候,就可能会导致,读到的值,不是最新值。
c. 有序性:这里只说指令重排序,虚拟机在把代码编译为指令后执行,处于优化的亩地,在保证结果不变的情况下,可能会调整指令的执行顺序。
valotile,能满足上述的可见性和有序性,但是无法保证其原子性。
可见性:是在修改后,强制把对变量的修改同步到系统内存。而其它cpu在读取自己的内部缓存中的值得时候,发现是valotile修饰的,会把内部缓存中的值,置为无效,然后从系统内存读取。
有序性,是通过内存屏障(可以理解为,在某些指令中,插入屏障指令,用以确保在向屏障指令后面继续执行的时候,其前面的所有指令已经执行完毕)来实现的,
扩展:在写单例模式时,通常会采用双层判断的方式,在最内层,instance = new Singleton()。其实这也是一个隐含的问题:这句赋值语句,其实是分三步来操作的:
a.为instance 分配内存
b.调用Singleto构造函数来初始化变量
c.instance指向上一步初始化的对象
在jvm做了指令重排序优化后,上述步骤b和c不能保证,可能出现c先执行,但是对象却没初始化,这时候其它线程判断的时候,发现是非null,但是使用的时候,却没有具体实例,导致报错。所以,我们可以使用valotile来修饰instance,避免该问题