面试官问我 volatile 是否存在伪共享问题?我懵逼了

简介: 在探讨 LongAdder 是如何解决伪共享问题之前,我们要先梳理清一个概念,什么是 伪共享 和 共享 ?共享在 Java 编程里面我们可以这样理解,有一个 Share 类,它有一个 value 的属性。如下:public class Share { int value; }复制代码我们初始化 Share 的一个实例,然后启动多个线程去操作它的 value 属性,此时的 Share 变量被多个线程操作的这种情况我们称之为 共享。大家都知道在不添加任何互斥措施的情况,多线程操作这个 Share 变量的 value 属性肯定存在线程安全性的问题。那有什么办法可以解决这个问题呢?

在探讨 LongAdder 是如何解决伪共享问题之前,我们要先梳理清一个概念,什么是 伪共享 和 共享 ?

共享在 Java 编程里面我们可以这样理解,有一个 Share 类,它有一个 value 的属性。如下:
public class Share {
int value;
}复制代码
我们初始化 Share 的一个实例,然后启动多个线程去操作它的 value 属性,此时的 Share 变量被多个线程操作的这种情况我们称之为 共享。

大家都知道在不添加任何互斥措施的情况,多线程操作这个 Share 变量的 value 属性肯定存在线程安全性的问题。那有什么办法可以解决这个问题呢?我们可以使用 volatile 和 CAS 技术来保证共享变量可以安全的被多个线程共享操作使用,不知道 volatile 和 CAS 技术点的同学可以参考往期文章 ReentranLock 实现原理居然是这样?。

但是由于 volatile 的引入,会带来一些问题。大家都知道 JMM(Java 内存模型)规范了 volatile 具有内存可见性和禁止指令重排序的语义。这俩条语义使得某个线程更新本地缓存中的 value 值后会将其他线程的本地缓存中的value 值失效,然后其他线程再次读取 value 值的时候需要去主存里面获取 value 值,这样即保证了 value 的内存可见性。
当然啦,这没有任何问题,但是由于线程本地缓存的操作是以缓存行为单位的,一个缓存行大小通常为 64B(不同型号的电脑缓存行大小会有不同)。因此一个缓存行中不会只单单存储 value 一个变量,可能还会存储其他变量。这样当一个线程更新了 value 之后,如果其他线程本地缓存中同样缓存了 value, value 所在的缓存行就会失效,这意味着该缓存行上的其他变量也会失效,那么线程对这个该缓存行上所有变量的访问都需要从主存中获取。我们都知道 CPU 访问主存的速度相对于访问缓存的速度有着数量级的差距,这就带了很大的性能问题,我们将这个问题称之为 伪共享。

理解了伪共享到底是什么鬼以后,我们来看看 Java 大师们是怎么解决这个问题的。在早期版本的 JDK 里面你应该见到过类似如下的代码:

public class Share {

volatile int value;    
long p1, p2, p3, p4, p5, p6;

}复制代码
你可能猜到了,定义了几个无用的变量作为填充物,他们会保证一个缓存行里面只保存了 Share 变量,这样更新 Share 变量的时候就不会存在伪共享问题了。但是这种方法存在什么问题呢?

首先基于每台运行 Java 程序的机器的缓存行大小可能不同,其次由于这些类似填充物的变量并没有被实际使用,可以被 JVM 优化掉,这样就失效了。

基于此,在 Java 8 的时候,官方给出了手机号码拍卖解决策略,这就是 Contended 注解。依赖于这个注解,我们在 Java 8 环境下可以这样改善代码:

public class Share {

@Contended
volatile int value;

}复制代码
使用如上注解,并且在 JVM 启动参数中加入 -XX:-RestrictContended,这样 JVM 在运行时就会自动的为我们的 Share 类添加合适大小的填充物(padding)来解决伪共享问题,而不需要我们手写变量来作为填充物了,这样就更加便捷优雅的解决了伪共享问题。悄悄的告诉你,LongAdder 就是使用 Contended 来解决伪共享问题哒。

好了,相信你已经了解了什么是伪共享问题,以及早期并发编程大师是如何解决伪共享问题的,最后我们也介绍了在 Java 8 中使用 Contended 来更优雅的解决伪共享问题。Contended 还提供了一个缓存行分组的功能,在上文中我们没有介绍,欢迎有兴趣的小伙伴们自行探索吧。

目录
相关文章
|
2月前
|
存储 缓存 安全
面试官:说说volatile底层实现原理?
面试官:说说volatile底层实现原理?
410 5
面试官:说说volatile底层实现原理?
|
5月前
|
Java
面试java并发~(lock、volatile、cas)
面试java并发~(lock、volatile、cas)
40 0
|
7月前
|
缓存 前端开发 Java
4年开发二面美团最终败给:volatile关键字作用和原理这道面试题
一位6年工作经验的小伙伴,在某里二面的时候被问到“volatile”关键字。然后,就没有然后了… 同样,还有一位4年的小伙伴,去某团面试也被问到“volatile 关键字“。然后,也没有然后了… volatile关键字是Java并发编程中的一个重要的关键字,这个问题确实问得比较底层了。难道大厂现在都这么卷了吗?那今天呢,我给大家分享一下我的理解,希望能够帮助到大家。
44 0
|
8月前
|
安全 Java
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
|
9月前
|
存储 安全 Java
【Java面试】说说synchronized和volatile的区别
【Java面试】说说synchronized和volatile的区别
146 0
|
9月前
|
传感器 缓存 安全
【Java面试】能说说你对volatile关键字的理解吗?
【Java面试】能说说你对volatile关键字的理解吗?
99 0
【Java面试】能说说你对volatile关键字的理解吗?
|
Java 编译器
Java 最常见的面试题:synchronized 和 volatile 的区别是什么?
Java 最常见的面试题:synchronized 和 volatile 的区别是什么?
|
SQL 缓存 Java
阿里面试官让我讲讲volatile,我直接从HotSpot开始讲起,一套组合拳拿下面试
阿里面试官让我讲讲volatile,我直接从HotSpot开始讲起,一套组合拳拿下面试
阿里面试官让我讲讲volatile,我直接从HotSpot开始讲起,一套组合拳拿下面试
|
安全 Java
JUC高频面试题---由Volatile印出来的一系列问题
JUC高频面试题---由Volatile印出来的一系列问题
84 0
JUC高频面试题---由Volatile印出来的一系列问题