Java教程

volatile解析

本文主要是介绍volatile解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

问题

(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解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!