volatile
是 Java 中一个非常重要的关键字,用于确保多线程环境中的共享变量的可见性。它确保了当一个线程修改某个变量的值时,其他线程可以立即看到这个修改,而不是使用线程本地的缓存。
volatile
关键字的作用在多线程编程中,线程会在本地缓存中存储变量的副本,这意味着某个线程修改了变量的值,另一个线程可能并不会立即看到这些变化。为了保证变量的变化能够立即对其他线程可见,我们使用 volatile
。
volatile
的核心作用:volatile
变量的值,其他线程会立即看到这个修改。volatile
修饰的变量会在访问时避免一些优化,比如 JVM 或 CPU 的指令重排序,从而保持代码执行的顺序性。volatile
的行为volatile
确保当一个线程修改了变量的值,其他线程能够立即看到这个变化。volatile
变量在编译和运行时会强制执行有序的操作,禁止对其进行指令重排序优化。volatile
?volatile
只能用于变量,而不能用于方法或类。它的作用是保证变量在多个线程中保持一致性。
volatile
变量保证可见性public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
// 线程1:修改 flag
new Thread(() -> {
try {
Thread.sleep(1000); // 模拟操作
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("线程1 修改 flag 为 true");
}).start();
// 线程2:读取 flag
new Thread(() -> {
while (!flag) {
// 当 flag 为 false 时,持续检查
}
System.out.println("线程2 检测到 flag 为 true,退出");
}).start();
}
}
flag
被声明为 volatile
,确保当线程 1 修改 flag
值为 true
时,线程 2 会立即看到这个变化并退出循环。volatile
,线程 2 可能由于使用线程本地的缓存,会永远看不到 flag
的变化,导致死循环。volatile
的工作原理volatile
变量的值时,这个值会立即刷新到主内存中,其他线程在访问这个变量时会从主内存获取最新的值。volatile
变量上,Java 保证所有线程直接访问主内存,不使用缓存。volatile
保证指令的执行顺序不会被改变,确保操作的顺序性。volatile
的限制尽管 volatile
解决了可见性问题,但它并不适用于解决所有的并发问题。特别是它无法保证原子性,即 volatile
变量不能用于替代锁来进行复杂的原子操作。
volatile
变量执行多个步骤的复合操作(如自增 i++
),无法保证原子性。例如:volatile int i = 0;
i++; // 非原子操作,i++ 分为读取 i、加 1、再写回 i,多个线程操作时会产生竞态条件
volatile
并不会像 synchronized
或 ReentrantLock
一样,确保对变量的操作是原子的。它只是保证可见性和禁止重排序。volatile
使用场景volatile
可以用来优化单例模式,确保单例对象被正确地初始化。volatile
在双重检查锁定的单例模式中,volatile
可以确保 instance
的正确创建。
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(); // 通过 volatile 保证 instance 的可见性
}
}
}
return instance;
}
}
instance
被声明为 volatile
,这可以防止因为 JVM 或 CPU 的指令重排而导致单例模式失效。volatile
保证了其他线程在第一次初始化 instance
时会看到它已经被正确地初始化。特性 | 说明 |
---|---|
可见性 | volatile 确保多线程中变量的修改对其他线程立即可见。 |
禁止重排序 | volatile 防止 JVM 或 CPU 重排变量的读写指令。 |
原子性 | volatile 不保证复合操作的原子性,如 i++ 。 |
使用场景 | 适用于控制标志、单例模式等需要保证可见性的场景。 |
限制 | 不能用于保证复合操作的原子性。 |