首页 > 其他 > 详细

volatile解析

时间:2021-06-09 12:44:35      阅读:20      评论:0      收藏:0      [点我收藏+]

问题

(1)volatile是如何保证可见性和禁止重排序?

(2)volatile的实现原理?

(3)volatile保证原子性吗?

 

volatile的特性

一个volatile变量自身具有以下三个特性:

  1. 可见性:即当一个线程修改了声明为volatile变量的值,新值对于其他要读该变量的线程来说是立即可见的。而普通变量是不能做到这一点的,普通变量的值在线程间传递需要通过主内存来完成。
  2. 有序性:volatile变量的所谓有序性也就是被声明为volatile的变量的临界区代码的执行是有顺序的,即禁止指令重排序。
  3. 受限原子性:这里volatile变量的原子性与synchronized的原子性是不同的,synchronized的原子性是指只要声明为synchronized的方法或代码块儿在执行上就是原子操作的。而volatile是不修饰方法或代码块儿的,它用来修饰变量,对于单个volatile变量的读/写操作都具有原子性,但类似于volatile++这种复合操作不具有原子性。所以volatile的原子性是受限制的。并且在多线程环境中,volatile并不能保证原子性。

 

volatile实现原理

volatile靠内存屏障实现可见性和禁止重排序。

技术分享图片

那么禁止指令重排序又是如何实现的呢?答案是加内存屏障。JMM为volatile加内存屏障有以下4种情况:

  1. 在每个volatile写操作的前面插入一个StoreStore屏障,防止写volatile与后面的写操作重排序。
  2. 在每个volatile写操作的后面插入一个StoreLoad屏障,防止写volatile与后面的读操作重排序。
  3. 在每个volatile读操作的后面插入一个LoadLoad屏障,防止读volatile与后面的读操作重排序。
  4. 在每个volatile读操作的后面插入一个LoadStore屏障,防止读volatile与后面的写操作重排序。

 

volatile不保证原子性

拿i++举例,看到i++被分解成了四条指令

getstatic //获取i当前的值并入栈
iconst_1 //入栈int类型的值1
iadd //将栈顶的两个值相加
putstatic //将相加的结果写回到i中

iconst_1和iadd在执行的过程中,这时并没有重新读取主内存中的最新值,可能i的值已经被其他线程修改了。所以volatile在i++这个场景中并不能保证其原子性

 

volatile的适用场景

volatile是在synchronized性能低下的时候提出的。如今synchronized的效率已经大幅提升,所以volatile存在的意义不大。

 

总结

volatile关键字是java提供的轻量级同步机制,保证了线程变量的可见性,和禁止指令重排。但不保证原子性。jvm创建线程时,都会为每个线程创建一个工作内存,工作内存是每个线程私有的。但是java规定所有的变量都存在主内存中。而单独的线程对变量的操作必须在工作内存中进行,所以首先要将变量从主内存中拷贝到自己的工作内存,然后对变量进行操作,操作完成后再将变量写回主内存****。因此不同的线程不能访问对方的线程,线程间的传值必须通过主内存来完成。

(1)volatile关键字可以保证可见性;

(2)volatile关键字可以保证有序性;

(3)volatile关键字不可以保证原子性;

(4)volatile关键字的底层主要是通过内存屏障来实现的;

(5)volatile关键字的使用场景必须是场景本身就是原子的;

volatile解析

原文:https://www.cnblogs.com/gaoweiBlog/p/14865662.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!