原创做品,能够转载,可是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7833881.htmlhtml
一、volatile简述java
听说,volatile是java语言中最轻量级的并发控制方式。缓存
volatile能够实现可见性、有序性,可是没法实现原子性,相对来讲:synchronized能够实现这三个并发特性,因此咱们可使用synchronized来代替volatile,可是一直以来synchronized都已重量级闻名,其实在jdk1.5以后的版本中,java对synchronized进行了针对性优化处理,其操做速度已经再也不是制约其是否可选择的因素,如今通常经过实际的状况来决定使用哪一种方式。多线程
二、volatile特性并发
2.1 可见性高并发
volatile能够保证变量的可见性,指的是什么呢?优化
可见性指的是在某一线程中对变量进行修改以后,其余线程能够当即发现并使用这个修改(一个线程的修改对其余线程可见)。atom
可见性的实现方式:volatile经过对java内存模型中主内存和工做内存交互方式的控制来实现。volatile确保一个线程对其修饰的变量的更改当即写入到主内存,同时确保每一次针对其修饰变量的读取操做直接从主内存中获取(即volatile强制将assign赋值操做和store、write操做绑定在一块儿,将use使用操做强制和read、load操做绑定在一块儿,这样assign以后必须执行store、write操做,use操做以前必须先执行read、load操做)这样就确保了其余线程读取到的变量的值是最新的(不熟悉这几个操做的同窗请先了解java内存模型)。spa
Volatile更底层的实现方式:一个线程对volatile变量进行了修改以后,会写到工做内存,这一步映射到底层就是cpu将计算结果保存到高速缓存中,这时会触发一个LOCK指令,这个指令有两个做用,第一,它会锁定总线或者缓存,将修改后的新值保存到系统内存中,映射到JVM就是保存到主内存中。第二,它会将其余CPU的高速缓存中的这个变量的值置为无效,映射到JVM就是讲其余线程的工做内存中保存的这个变量值置为无效,这样在这些其余线程要对变量进行操做时,读取变量时发现工做内存中的值是无效的,随即从主内存从新读取,并保存到工做内存。线程
这里还要说说明一点,不管是主内存仍是工做内存(系统内存仍是高速缓存)都只是保存数据的部件或位置,全部针对变量的操做所有须要在CPU中进行,因此即便将数据从主内存(系统内存)读取到工做内存(高速缓存)中以后,想要操做,还须要从工做内存(高速缓存)中读取到CPU中的寄存器中进行计算。
2.2 非原子性
注意:volatile能够实现可见性,可是没法实现原子性。单个volatile变量的读写操做具备原子性,可是复合操做是与java代码相关的,并非volatile这么一个关键字既能够控制得了的。(请将可见性和原子操做区分开来,我以前就混淆在一块儿,分开以后当即通透了)
java中实现原子操做的方式仍是有不少的,可是并不包含volatile,简单的实现方式有:atomic包下的原子操做(经过CAS实现),基本数据类型的读写操做,加锁(synchronized或者Lock)实现等。
java中经典的非原子操做如自增实现,普通的i++操做看似只有一句话,可是编译成机器指令以后拥有多少行,不可知,确定不是一句就能实现的,这么多命令要执行固然没法保证原子性,这时候咱们能够用AtomicInteger和AtomicLong原子操做类的getAndIncrement()方法来实现,固然是用synchronized加锁一样能够实现。
2.3 有序性
volatile的另外一个做用就是避免重排序优化,使用内存屏障的方式来实现禁止重排序优化。在单线程环境中固然没有必要禁止重排序优化,可是在多线程环境中指令重排序后执行就可能会出错,好比线程A中须要检测线程B中的某一个变量的值,依据这个值来进行某些操做。若是没有使用volatile修饰该变量,线程B中针对这个变量的操做就可能会发生重排序,可能会提早执行,这时一旦操做提早执行,那么线程A就能够会提早获得这个变量的值(或许是在一些线程A的准备工做还未所有准备好的状况,假设这些准备工做在线程B中定义,可是与变量操做无依赖关系,一旦变量操做提早,这些准备工做就会滞后,这时线程A就会在准备工做还没有完成的状况下启动执行后行代码,致使出错)。为变量加上volatile修饰以后,就会禁止其操做的指令重排序优化,保证因此的准备工做所有执行完成以后再进行变量操做,而后线程A在准备齐备的状况下启动,得以正常执行。
volatile有序性的实现原理是什么呢?
volatile底层经过内存屏障的方式来禁止重排序优化,具体来讲,JMM采用的是保守策略,所谓保守策略,就是经过冗余的内存屏障来保证全部影响Volatile功能的重排序所有被禁止,保证volatile功能的完整性,此处冗余的意思就是,可能会存在多余的内存屏障,但这种多余的内存屏障是不影响操做执行的,或者说是有的内存屏障所禁止的重排序操做若是实际发生了重排序也不会影响操做结果的状况,可是这种冗余的内存屏障能够排除那种任何特殊状况来确保volatile功能的完整性。
内存屏障包括:
(1)volatile变量写操做以前的storestore屏障,这个屏障保证全部在volatile写操做以前的任何操做都不能被重排序到volatile写操做以后,确保volatile变量写操做的正确性,由于全部直接或间接的修改都在写操做以前完成了,那么写的变量值必定是最终的正确值。
(2)volatile变量写操做以后的storeload屏障,这个屏障保证全部在volatile写操做以后的任何操做都不能被重排序到volatile写操做以前,确保volatile变量写操做的正确性,其实这里真正禁止的是其后可能出现的volatile读写操做被重排序到这个写操做以前。
(3)volatile变量读操做以后的loadload屏障,这个屏障保证全部在volatile读操做以后的任何读操做都不会被重排序到volatile读操做以前,确保volatile变量读操做的正确性。
(4)volatile变量读操做以后的loadstore屏障,这个屏障保证全部在volatile读操做以后的任何写操做都不会被重排序到volatile读操做以前,确保volatile变量读操做的正确性。
上面的内容并很差理解和记忆,咱们能够总结以下:
全部Volatile写操做以前的操做禁止重排序到该写操做以后;
全部volatile读操做以后的操做禁止重排序到该读操做以前;
volatile写操做以后是volatile读操做时,禁止重排序。
记住上述三点就能够了!
三、volatile使用
在涉及到并发操做的状况下,能够优先考虑是否可使用volatile来解决问题,适合的场景以下:
在只涉及可见性,针对变量的操做只是简单的读写(保证操做的原子性)的状况下可使用volatile来解决高并发问题,若是这时针对变量的操做是非原子的操做,这时若是只是简单的i++式的操做,可使用原子类atomic类来保证操做的原子性(采用CAS实现),若是是复杂的业务操做,那么舍弃volatile,采用锁来解决并发问题(synchronized或者Lock)。
四、volatile总结
volatile能够实现可见性和有序性,没法实现原子性 ,简单的原子操做能够委托给其余方式进行实现,复杂的原子操做须要借助锁来实现,这时候彻底没有必要加上volatile了,由于锁已经包含了volatile的功能。