开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情 这也是第20篇文章
对于某一类书,我会带有一定的目的性去看。比如当我看到volatile的时候,我会想,如果别人问起我这个知识点(比如我未来的面试官),我会如何向ta解释呢?我这么说了以后,ta还有可能会问什么问题呢?于是就有了这个系列的文章。但是我不敢在标题写上“面试“一类的字样,因为这样未免夸大了点,毕竟我自己还没有去面试过;而且也显得功利心太重了。但是文章的作用,相信大家看完之后也能体会得到。
volatile是线程安全的吗
volatile并不能保证线程安全。
那volatile有什么作用或者说特点?
- 解决多核CPU高速缓存导致的变量不同步 这涉及到了线程安全的可见性问题。
- 解决指令重排序的问题
- 不保证操作的原子性
线程安全的可见性是指什么?
当多个线程在访问同一个变量时,如果其中一个线程修改了变量的值,那么其他线程应该能 立即 看到修改后的值。
能否从原理的角度谈谈这是如何实现的?
内存屏障。
- 当CPU写数据时,如果发现一个变量在其他CPU上有副本,会发出信号量通知其他CPU将该副本对应的缓存行置为无效状态。
- 当其他CPU读取到变量副本时,会发现该缓存行无效,并重新从主存读取变量。
那volatile如何解决指令的重排序问题?
首先要理解什么是重排序。
重排序是指:CPU在运行期间会对指令进行优化,将某些没有依赖关系的指令的顺序重排。 在多线程的环境下,重排序之后可能会出现问题,因此多线程如果想保证线程安全,必须得保证有序性。也就是说程序执行的顺序完全按照代码执行的顺序。
通过对变量用volatile修饰,CPU就不会对它进行重排序优化。
你刚刚提到volatile不保证原子性,详细说说?
简单的说,修改volatile变量分为四步:
- 读取volatile变量到local
- 修改变量值
- local值写回
- 插入内存屏障,即lock指令,让其他线程可见
但在前三步中,取值和写回之间,不能保证没有其他线程修改,因此不能保证原子性。
参考资料
- Java核心技术卷1
- volatile为什么不能保证原子性?