java关键字volatile
2023-12-21 07:33:49
引言
在java语言编程中,我们会使用到很多修饰符(也可以说是关键字),比如说public、class、final、static等,他们都是各自的作用,今天我们来介绍介绍volatile这个修饰符。
概念
1、作用与可用范围
volatile只能用于修饰变量,不能用于修饰方法或者类等。那么此时这个变量就拥有了两层语义了。
- 当前变量对所有线程可见
这里的所有线程指的是操作或者使用这个变量的线程 - 禁止指令重排序
2、简单流程
- A线程从主内存中读取了i的值到A工作内存中
- B线程修改B工作内容中的i为j,并更新到主内存中此时主内存值为j
- 此时A的工作内存中的i值失效
- A同步更新主内存中的j,此时A工作空间的值为j
3、原子性
此关键字不会保证原子性
举个简单的例子:
// 定义一个变量
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不会保证原子性导致的
- A线程最开始拿到0并加载到工作内存,执行了自增操作
- 此时CPU切换至B线程,B拿到的也是0,因为A线程并没有刷新到主内存中
- B线程执行了自增,并且更新到主内存,此时主内存为1
- 此时CPU又切换回A线程,并且把1也刷新至主内存
- 理论结果值应该为2,但是实际结果为1
应用
单例模式的双重检查
这里主要就是禁止指令重排序。因为编译器和处理器会优化程序性能,进行指令重排序。
比如说我们在单例模式中会有这样一段代码
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、其他工作内存的缓存失效
文章来源:https://blog.csdn.net/m0_56418245/article/details/135107166
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!