一位6年工作经验的小伙伴,在某里二面的时候被问到“volatile”关键字。然后,就没有然后了…
同样,还有一位4年的小伙伴,去某团面试也被问到“volatile 关键字“。然后,也没有然后了…
volatile关键字是Java并发编程中的一个重要的关键字,这个问题确实问得比较底层了。难道大厂现在都这么卷了吗?那今天呢,我给大家分享一下我的理解,希望能够帮助到大家。
另外,我准备了一份500页的PDF面试题解析配套文档,
如何获取? :
扫描文章底部二维码领取!
1、volatile的作用
volatile 关键字呢,有两个作用:
第1个,是可以保证在多线程环境下共享变量的可见性。
第2个,是可以屏蔽在多线程环境下CPU的指令重排。
下面,我给大家详细介绍一下可见性和屏蔽指令重排以及volatile的工作原理。
2、原理分析
先来看变量的可见性,简单来说,就是指当某一个线程对共享变量的修改,其他线程可以立刻看到修改之后的值。其实这个可见性问题,我认为本质上是由以下两个方面造成的。
首先是,CPU的高速缓存。在 CPU 里面设计了三级缓存去解决 CPU 运算效率和内存 IO 效率问题,但是有带来了缓存的一致性问题,而在多线程并行执行的情况下,缓存一致性就会导致可见性问题。
所以,对于增加了 volatile 关键字修饰的共享变量,JVM 虚拟机会自动增加一个#Lock 汇编指令,这个指令会根据 CPU 型号自动添加总线锁或/缓存锁。
我简单介绍一下这两种锁:
总线锁是锁定了 CPU 的前端总线,从而导致在同一时刻只能有一个线程去和内存通信,这样就避免了多线程并发造成的可见性。
缓存锁是对总线锁的优化,因为总线锁导致了 CPU 的使用效率大幅度下降,所以缓存锁只针对 CPU 三级缓存中的目标数据加锁,缓存锁是使用 MESI 缓存一致性来实现的。
然后,就是屏蔽指令重排,就是指屏蔽CPU指令重排序。意思是在多线程环境下,CPU指令的编写顺序和执行顺序不一致,从而导致可见性问题,为了提升 CPU 的利用率,CPU引入了StoreBuffer 机制,而这一种优化机制会导致 CPU 的乱序执行。当然为了避免这样的问题,CPU 提供了内存屏障指令,上层应用可以在合适的地方插入内存屏障来避免 CPU 指令重排序问题。而volatile就是通过设置内存屏障来禁止指令重排。
volatile内存屏障实现原理主要从以下两个方面来分析:
第1个是:volatile会在变量写操作的前后加入两个内存屏障,来保证前面的写指令和后面的读指令是有序的,如图所示:
第2个是:volatile在变量的读操作后面插入两个指令,禁止后面的读指令和写指令重排序。,如图所示:
volatile其实可以看作是轻量级的synchronized,虽然说volatile不能保证原子性,但是如果在多线程下的操作本身就是原子性操作(例如赋值操作),那么使用volatile会优于synchronized。以上就是我对volatile 关键字的理解。
并发编程是每个程序员必须要掌握好的领域,它里面涵盖的设计思想、和并发问题的解决思路、以及作为一个并发工具,都是非常值得深度研究的。
最后,我把往期分享的视频全部整理成了1份20W字的文档,希望能够以此来提高各位粉丝的通过率