Java volatile关键字介绍
Java中的`volatile`是一种关键字,用于保证多个线程之间对共享变量的可见性。当一个变量被声明为`volatile`时,对这个变量的读取和写入操作都会直接从主内存中读取和写入,而不是从线程本地缓存中读取和写入。具体来说,volatile
关键字能够保证以下两个特性:
- 可见性:当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去,这个写会操作会导致其他线程中的volatile变量缓存无效。
- 有序性:使用volatile关键字修饰共享变量通过禁止指令重排序来保证有序性。
volatile保证可见性的代码演示
public class VolatileExample {
private volatile Integer num = 0;
public void run(){
System.out.println("running...");
for (int i = 0; i < 100; i++) {
try {
num++;
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public void stop(){
while (true) {
if (num==100){
System.out.println("num is 100, stop!");
return;
}
}
}
public static void main(String[] args) {
/*
* 在类中定义一个volatile修饰的变量初始为0
* 线程1 执行run方法将num递加到100
* 线程2 执行stop方法判断num是否等于100 等于100时退出
* 如果线程2先执行 且可以查看到num==100,则表明volatile可以保证不同线程对同一个变量可见
* */
VolatileExample ve = new VolatileExample();
new Thread(()->{
ve.stop();
}).start();
new Thread(()->{
ve.run();
}).start();
}
}
volatile保证可见性和有序性的原理
Java中的volatile
关键字主要是通过内存屏障(Memory Barrier)来实现可见性和有序性的。
内存屏障是一种硬件或软件机制,用于限制编译器和处理器对内存访问的优化,以确保对内存操作的顺序和可见性。Java中的内存屏障有以下三种:
- Load Barrier(读屏障):确保本条屏障之前的所有读操作都完成。
- Store Barrier(写屏障):确保本条屏障之前的所有写操作都完成。
- Full Barrier(完全屏障):同时包含读屏障和写屏障的效果,确保本条屏障之前的所有读/写操作都完成。
在使用volatile
关键字修饰变量时,Java编译器会在生成字节码时插入内存屏障,以确保对volatile
变量的读写操作都是有序的,并且保证了变量的可见性。
具体来说,当一个线程对volatile
变量进行写操作时,Java虚拟机会在写操作的前后插入写屏障。这个写屏障会强制将写入操作刷新到主内存中,同时会使其他线程中的本地缓存失效。同样地,当一个线程对volatile
变量进行读操作时,Java虚拟机会在读操作的前后插入读屏障。这个读屏障会强制从主内存中读取变量的值,而不是从线程本地缓存中读取。