面试题答案
一键面试线程对共享变量读写操作保证可见性的原理
- 缓存一致性协议:在多核处理器环境下,每个处理器核心都有自己的高速缓存(L1、L2等)。当线程读取共享变量时,会先从主内存将变量读取到自己核心的高速缓存中。当线程对共享变量进行修改时,会先修改自己高速缓存中的副本,然后通过缓存一致性协议将修改同步回主内存。其他线程在读取共享变量时,会发现自己高速缓存中的副本失效,从而从主内存重新读取最新的值。
- Java内存模型的规定:Java内存模型(JMM)定义了线程和主内存之间的抽象关系。线程之间的共享变量存储在主内存中,每个线程都有自己的工作内存,工作内存中保存了该线程使用到的共享变量的副本。线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接操作主内存。当线程修改共享变量后,需要将修改后的值刷新到主内存,其他线程在使用共享变量前,需要从主内存重新读取最新的值。
volatile关键字的作用
- 保证可见性:当一个变量被声明为volatile时,线程对该变量的修改会立即刷新到主内存,并且其他线程对该变量的读取都会从主内存获取最新的值,从而保证了其他线程能及时看到更新。例如:
public class VolatileExample {
private volatile int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
在上述代码中,当一个线程调用setValue
方法修改value
时,修改会立即刷新到主内存,其他线程调用getValue
方法时能获取到最新的值。
2. 禁止指令重排序:在Java中,为了提高程序的执行效率,编译器和处理器会对指令进行重排序。但是重排序可能会导致程序在多线程环境下出现错误。volatile关键字可以禁止指令重排序,确保volatile变量的写操作在其之前的操作都执行完毕后才执行,并且volatile变量的读操作在其之后的操作都在读取到最新值之后才执行。例如:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在上述双重检查锁定实现单例模式的代码中,instance
被声明为volatile,防止了指令重排序导致其他线程可能获取到一个未完全初始化的instance
对象。