在java语言编程中,我们会使用到很多修饰符(也可以说是关键字),比如说public、class、final、static等,他们都是各自的作用,今天我们来介绍介绍volatile这个修饰符。
volatile只能用于修饰变量,不能用于修饰方法或者类等。那么此时这个变量就拥有了两层语义了。
此关键字不会保证原子性
举个简单的例子:
// 定义一个变量
private volatile static int i = 0;
/**
- 创建新的线程执行i自增的结果
*/
public static void testAdd() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
count++;
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
System.out.println(count);
}
以上代码执行完理论上来说应该是 1000*10=10000,但是实际执行完后没有10000,就是因为volatile不会保证原子性导致的
这里主要就是禁止指令重排序。因为编译器和处理器会优化程序性能,进行指令重排序。
比如说我们在单例模式中会有这样一段代码
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
获取单例模式可以分为三步
1、分配内存
2、初始化对象
3、变量指向内存地址
如果这里不使用volatile,在2、3步可能会指令重排序,导致最终拿到的对象可能是还没有初始化完成的对象
1、确保指令不会重排序
2、强制刷新至主内存
3、其他工作内存的缓存失效