面试题:Java 中 volatile 关键字的作用是什么?

volatile 是 Java 中的一个关键字,用于声明变量可以在程序的其他部分被修改,尤其是在多线程环境下。它的主要作用是保证变量的可见性和禁止指令重排序优化,但不会提供原子性操作。下面详细解释 volatile 的作用:

1. 可见性

在多线程环境中,当一个线程对 volatile 变量进行写操作时,这个写操作会立即被刷新到主内存中,而不是停留在 CPU 缓存中。同样地,当另一个线程尝试读取这个 volatile 变量时,它将直接从主内存中读取值,而不是使用自己工作内存中的副本。这意味着所有线程都能看到最新的值。

public class SharedObject {
    public volatile int sharedVar = 0;
}

在这个例子中,如果一个线程改变了 sharedVar 的值,那么这个变化会被立刻反映到主内存中,并且任何其他线程在访问 sharedVar 时都会看到这个更新后的值。

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;
    }
}

这里,volatile 关键字确保了 instance 的初始化完全发生在其引用被设置之前,避免了潜在的重排序问题。

注意事项

  • 非原子性:尽管 volatile 提供了可见性和禁止指令重排序的功能,但它并不提供原子性操作。例如,对于复合操作(如 i++),即使 i 是 volatile 类型,也不能保证线程安全。
  • 适用场景volatile 适用于那些不需要原子性操作的简单状态标志或类似情况。对于需要原子性的复杂操作,应该考虑使用同步机制(如 synchronized 或者 java.util.concurrent.atomic 包下的类)。

总结来说,volatile 主要用于确保变量的可见性和禁止特定类型的指令重排序,适用于对共享变量的轻量级同步需求。

THE END
喜欢就支持一下吧
点赞13 分享