java关键字volatile

引言

在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、其他工作内存的缓存失效

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值