面试题答案
一键面试volatile关键字对变量可见性和有序性的影响机制
-
可见性影响机制
- 在Java内存模型中,每个线程都有自己的工作内存,线程对变量的操作(读取、修改等)都在工作内存中进行。而主内存是所有线程共享的。当一个线程修改了共享变量的值,它不会立即同步到主内存,其他线程也不会立即从主内存中获取最新值。
volatile
关键字的作用是当一个线程修改了volatile
修饰的变量时,会立即将修改后的值刷新到主内存中。同时,当其他线程读取这个volatile
修饰的变量时,会直接从主内存中获取最新值,而不是从自己的工作内存中获取旧值。这就保证了不同线程之间对volatile
修饰变量的可见性。
-
有序性影响机制
- Java编译器和处理器为了提高程序执行性能,会对指令进行重排序。重排序可能会导致程序执行顺序与代码编写顺序不一致,但在单线程环境下不会影响程序的最终执行结果。
volatile
关键字禁止了指令重排序优化。对于volatile
修饰的变量,在其写操作之前的指令不会被重排序到写操作之后,在其读操作之后的指令不会被重排序到读操作之前。这保证了在多线程环境下,volatile
变量相关操作的有序性。
多线程环境下使用volatile关键字和不使用时变量可见性差异表现的示例
- 不使用volatile关键字的情况
public class NoVolatileExample {
private static int value = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (value == 0) {
// 线程1在等待value变化
}
System.out.println("Thread 1: value is changed to " + value);
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = 1;
System.out.println("Thread 2: value is set to " + value);
});
thread1.start();
thread2.start();
}
}
在这个例子中,thread1
可能会一直循环,因为它的工作内存中的value
值可能不会及时更新。thread2
修改了value
的值并写入主内存,但thread1
可能继续从自己的工作内存中读取旧的value
值(为0),导致无法结束循环。
- 使用volatile关键字的情况
public class VolatileExample {
private static volatile int value = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (value == 0) {
// 线程1在等待value变化
}
System.out.println("Thread 1: value is changed to " + value);
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = 1;
System.out.println("Thread 2: value is set to " + value);
});
thread1.start();
thread2.start();
}
}
在这个例子中,由于value
被volatile
修饰,当thread2
修改value
的值为1并刷新到主内存后,thread1
会立即从主内存中获取到最新的value
值(为1),从而结束循环并打印出相应信息。这体现了volatile
关键字保证变量在多线程间的可见性。