volatile


前言

在讲解volatile之前,我们先来了解一下内存可见性问题

volatile是java中比较重要的一个关键字,它是轻量级锁,有两个作用

保证共享变量在不同线程之间可见
禁止指令重排序

内存可见性问题

首先来看看在多线程下处理共享变量的java内存模型:
在这里插入图片描述

Java内存模型规定,将所有的变量都存放在主内存中,当线程使用变量时,会把主内存里面的变量复制到自己的工作空间或者叫作工作内存,线程读写变量时操作的是自己工作内存中的变量。

Java内存模型是一个抽象的概念,那么在实际实现中线程的工作内存是什么呢?看下图:

在这里插入图片描述

图中所示是一个双核CPU系统架构,每个核有自己的控制器和运算器,其中控制器包含一组寄存器和操作控制器,运算器执行算术逻辑运算。每个核都有自己的一级缓存,在有些架构里面还有一个所有CPU都共享的二级缓存。

那么Java内存模型里面的工作内存,就对应这里的L1或者L2缓存或者CPU的寄存器

当一个线程操作共享变量时,它首先从主内存复制共享变量到自己的工作内存,然后对工作内存里的变量进行处理,处理完后将变量值更新到主内存。

那么假如线程A和线程B同时处理一个共享变量,会出现什么情况?我们使用图2所示CPU架构,假设线程A和线程B使用不同CPU执行,并且当前两级Cache都为空,那么这时候由于Cache的存在,将会导致内存不可见问题,具体看下面的分析:

  • 线程A首先获取共享变量1的值,由于两级Cache都没有命中,所以加载主内存中1的值,假如为0。然后把var1=0的值缓存到两级缓存,线程A修改var1的值为1,然后将其写入两级Cache,并且刷新到主内存。线程A操作完毕后,线程A所在的CPU的两级Cache内和主内存里面的var1的值都是1。
  • 线程B获取var1的值,首先一级缓存没有命中,然后看二级缓存,二级缓存命中了,所以返回var1= 1;到这里一切都是正常的,因为这时候主内存中也是var1=1。然后线程B修改var1的值为2,并将其存放到线程2所在的一级Cache和共享二级Cache中,最后更新主内存中var1的值为2,到这里一切都是好的。
  • 线程A这次又需要修改var1的值,获取时一级缓存命中,并且var1=1,到这里问题就出现了,明明线程B已经把var1的值修改为了2,为何线程A获取的还是1呢?这就是共享变量的内存不可见问题,也就是线程B写入的值对线程A不可见。

在java中,volatile关键字就可以解决这个问题,接下来我们来看看它是怎么解决的:

volatile特性一:保证共享变量内存可见

volatile具体是怎么实现共享变量线程间可见的呢?简单来说可以分为两点:

  1. 当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存
  2. 当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。

volatile特性二:禁止指令重排序

volatile禁止指令重排序使通过内存屏障来实现的,通过插入内存屏障进制在内存屏障前后的指令执行重排序优化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值