1、概述
在Java中,volatile是一个关键字,它用于确保多线程环境下的变量可见性;当一个变量被声明为volatile时,它可以确保所有线程都能正确地读取该变量的最新值。
注: a、可以确保变量的可见性,但它不能保证变量的原子性;
b、使用volatile时会禁止指令重排
2、volatile关键字的特性
a、可见性(保证)
在多线程环境中,每个线程都有自己的工作内存,每个线程对变量的修改都只存在于其自己的工作内存中,而不是直接修改在主内存(主存)中的变量。这可能会导致一个线程对变量的修改在其他线程中不可见,也就是出现了所谓的缓存不一致问题,通过volatile可以解决这个问题;
volatile 如何实现变量可见性?
volatile关键字的作用是禁止CPU缓存和编译器优化,从而确保每次读取变量时都会直接从主内存中获取最新值,而不是从工作内存中读取。这样,当一个线程修改了变量的值后,其他线程能够立即看到最新的值,从而实现了变量的可见性;
public class VolatileTest1 {
private volatile static Boolean flag = true;
public static void main(String[] args) {
new Thread(() -> {
while (flag) {
}
System.out.println("线程一运行");
}).start();
new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("线程二运行");
flag = false;
Thread.sleep(1000);
System.out.println("线程二运行1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
b、原子性(不保证)
概述:原子性指的是某个线程正在执行某个操作时,中间不可以被加塞或分割,要么整体成功,要么整体失败。
public class VolatileAtomicityTest {
public static volatile int i = 0;
public static void main(String[] args) {
int threadPoolSize = 20;
ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
for (int j = 0; j < threadPoolSize; j++) {
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"----->"+i);
i++;
});
}
executorService.shutdown();
}
}
根据上面的代码执行结果判断,volatile不能保证操作的原子性;一个线程在修改i的时候结果还没有告诉主内存,另一个线程又从主内存获取了i;索引执行i++操作是数据的原子性没有保证;可以通过加锁(synchronized,lock)或者原子类(Atomic)来确保数据安全
c、禁止指令重排
在多线程环境下,为了提高程序的执行效率,编译器和处理器可能会对指令进行重排。然而,这种重排可能会导致多个线程之间的操作出现竞态条件,从而产生不可预测的结果。
使用volatile关键字可以禁止指令重排。当一个变量被声明为volatile时,它会告诉编译器和处理器,这个变量可能会被多个线程同时访问,因此不能对其进行重排。这可以确保在多线程环境下,操作的顺序不会发生改变,从而避免竞态条件。
参考文献:
https://www.cnblogs.com/zhongqifeng/p/14684028.html
java汇编工具使用:
https://blog.csdn.net/z435128234/article/details/128099880