个人总结 -- volatile关键字

记录一下,我前一段时间面试被问到比较多的一个问题--volatile关键字

一、volatile特性:

volatile是Java虚拟机提供的轻量级的同步机制。主要有三大特性:

  •  保证可见性

  •  不保证原子性

  •  禁止指令重排序

1、保证可见性

640?wx_fmt=png

举个例子,比如说有两个线程,他们的代码里都需要读取data这个变量的值,那么他们都会从主内存里加载data变量的值到自己的工作内存,然后才可以使用那个值,这样的话,每个线程拿到的data都是0。
这样,在线程代码运行的过程中,对data的值都可以直接从工作内存里加载了,不需要再从主内存里加载了。
为什么让线程每次都从主内存加载变量的值?
因为线程运行的代码对应的是一些指令,是由CPU执行的!但是CPU每次执行指令运算的时候,也就是执行我们写的代码的时候,要是每次需要一个变量的值,都从主内存加载,性能会比较差!

根据上面的图,可能看到一个问题:

假如说线程1修改了data变量的值为1,然后将这个修改写入自己的本地工作内存。那么此时,线程1的工作内存里的data值为1。然而,主内存里的data值还是为0,线程2的工作内存里的data值还是0啊!

这就导致,线程1和线程2其实都是在操作一个变量data,但是线程1修改了data变量的值之后,线程2是看不到的,一直都是看到自己本地工作内存中的一个旧的副本的值!

这就是所谓的java并发编程中的可见性问题。

多个线程并发读写一个共享变量的时候,有可能某个线程修改了变量的值,但是其他线程看不到,也就是对其他线程不可见

 

Java就是利用volatile来提供可见性的。当一个变量被volatile修饰时,那么对它的修改会立刻刷新到主存,当其它线程需要读取该变量时,会去内存中读取新值。而普通变量则不能保证这一点。

其实通过synchronized和Lock也能够保证可见性,线程在释放锁之前,会把共享变量值都刷回主存,但是synchronized和Lock的开销都更大。

2、不保证原子性

Java中,对基本数据类型的读取和赋值操作是原子性操作,所谓原子性操作就是指这些操作是不可中断的,要做一定做完,要么就没有执行。比如:

i = 2;  j = i;  i++;  i = i + 1;

上面4个操作中,i=2是读取操作,必定是原子性操作,j=i你以为是原子性操作,其实吧,分为两步,一是读取i的值,然后再赋值给j,这就是2步操作了,称不上原子操作,i++和i = i + 1其实是等效的,读取i的值,加1,再写回主存,那就是3步操作了。所以上面的举例中,最后的值可能出现多种情况,就是因为满足不了原子性。

volatile为什么不保证原子性?

举个例子:i++在JMM中分为三个指令:

  • 从主内存中拿到原始值
  • 在线程工作内存中进行加1操作
  • 把累加后的值写回主内存
如果第二个线程在第一个线程读取旧值和写回新值期间读取i的值,
那么第二个线程就会与第一个线程看到同一个值,并执行相同值的加1操作,这也就造成了线程安全问题。

原子性问题,得依赖Synchronized、ReentrantLock等加锁机制来解决。

3、禁止指令重排序

int a=10; //1
int b=20; //2
int c= a+b; //3

以上这段简单的代码,理想情况下它的执行顺序是:1>2>3。但有可能经过 JVM 优化之后的执行顺序变为了 2>1>3。这就叫做指令重排。

但是不管 JVM 怎么优化,前提都是保证单线程中最终结果不变的情况下进行的

但是在一些获取状态或者初始化等情况下,就有可能出现问题。

所以加上 volatile之后可以防止这样的重排优化,保证业务的正确性。

volatile的写-读与锁的释放-获取有相同的内存效果。

volatile写-读的内存语义:

当写一个volatile变量时,JMM会把线程A对应的本地内存中的共享变量值刷新到主内存。
当读一个volatile变量时,JMM会把线程B对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

线程A写一个volatile变量,随后线程B读这个volatile变量,实质上是线程A通过主内存向线程B发送消息。

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序

  • volatile写插入内存模型:

  • volatile读插入内存模型:

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值