面试题答案
一键面试一、volatile关键字底层实现原理
- JVM层面
- 内存屏障:JVM在生成字节码时,会在volatile变量的读操作和写操作前后插入内存屏障。
- 写操作前:插入StoreStore屏障,确保在volatile写之前,前面的所有普通写操作都已刷新到主内存。
- 写操作后:插入StoreLoad屏障,防止volatile写之后的读操作被重排序到写操作之前,保证volatile写对其他线程的可见性。
- 读操作后:插入LoadLoad屏障,保证在volatile读之后的读操作不会被重排序到volatile读之前。
- 读操作后:插入LoadStore屏障,防止volatile读之后的写操作被重排序到读操作之前。通过这些内存屏障,JVM保证了volatile变量的可见性和禁止重排序特性。
- 内存屏障:JVM在生成字节码时,会在volatile变量的读操作和写操作前后插入内存屏障。
- 硬件层面
- 缓存一致性协议:在多处理器系统中,每个处理器都有自己的高速缓存。当一个处理器修改了共享变量的值并将其写回主内存时,其他处理器通过缓存一致性协议(如MESI协议),可以感知到该变量的变化,并使自己缓存中的该变量副本失效。当其他处理器再次读取该变量时,会从主内存重新加载最新的值,从而保证了不同处理器之间共享变量的可见性。对于volatile变量,硬件在处理时会严格遵循这些缓存一致性机制,确保其可见性。
二、高并发场景下volatile变量的性能优化
- 缓存行对齐
- 原理:现代CPU缓存是以缓存行为单位进行管理的,通常缓存行大小为64字节。如果多个变量被映射到同一缓存行,当其中一个变量被修改时,整个缓存行都需要被更新,这可能导致其他处理器缓存中该缓存行的失效,从而增加了缓存未命中的开销。缓存行对齐就是通过填充字节等方式,使不同的volatile变量分布在不同的缓存行中,减少缓存行争用。
- 实际项目应用:
- Java 8之前:可以通过手动填充字节的方式实现缓存行对齐。例如,定义一个类,在volatile变量前后填充一定数量的字节,使每个volatile变量独占一个缓存行。
public class VolatilePadding {
private long p1, p2, p3, p4, p5, p6, p7;
public volatile long value;
private long p8, p9, p10, p11, p12, p13, p14;
}
- **Java 8及之后**:可以使用`@sun.misc.Contended`注解,该注解可以自动实现缓存行对齐。例如:
import sun.misc.Contended;
public class VolatilePadding {
@Contended
public volatile long value;
}
需要注意的是,使用@sun.misc.Contended
注解时,需要在启动JVM时添加-XX:-RestrictContended
参数,以启用该功能。通过缓存行对齐,可以减少缓存行争用,提高高并发场景下对volatile变量的访问性能。