原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7833881.html

1、volatile简述

  据说,volatile是java语言中最轻量级的并发控制方式。

  volatile可以实现可见性、有序性,但是无法实现原子性,相对来说:synchronized可以实现这三个并发特性,所以我们可以使用synchronized来代替volatile,但是一直以来synchronized都已重量级闻名,其实在jdk1.5之后的版本中,java对synchronized进行了针对性优化处理,其操作速度已经不再是制约其是否可选择的因素,现在一般通过实际的情况来决定使用哪种方式。

2、volatile特性

2.1 可见性

  volatile可以保证变量的可见性,指的是什么呢?

  可见性指的是在某一线程中对变量进行修改之后,其他线程可以立即发现并使用这个修改(一个线程的修改对其他线程可见)。

  可见性的实现方式:volatile通过对java内存模型中主内存和工作内存交互方式的控制来实现。volatile确保一个线程对其修饰的变量的更改立即写入到主内存,同时确保每一次针对其修饰变量的读取操作直接从主内存中获取(即volatile强制将assign赋值操作和store、write操作绑定在一起,将use使用操作强制和read、load操作绑定在一起,这样assign之后必须执行store、write操作,use操作之前必须先执行read、load操作)这样就确保了其他线程读取到的变量的值是最新的(不熟悉这几个操作的同学请先了解java内存模型)。

  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变量读操作的正确性。

理解:上面1里面的storestore的第二个store表示的就是当前的volatile写操作,第一个store表示的那就是当前volatile写操作之前的写操作,其他几个屏障也正是如此定义的,2中的表示是当前volatile写操作之后的读操作,3和4表示volatile读操作之后的读和写操作。

  上面的内容并不好理解和记忆,我们可以总结如下:

    所有Volatile写操作之前的操作禁止重排序到该写操作之后;

    所有volatile读操作之后的操作禁止重排序到该读操作之前;

    volatile写操作之后是volatile读操作时,禁止重排序。

  记住上述三点就可以了!

3、volatile使用

  在涉及到并发操作的情况下,可以优先考虑是否可以使用volatile来解决问题,适合的场景如下:

    在只涉及可见性,针对变量的操作只是简单的读写(保证操作的原子性)的情况下可以使用volatile来解决高并发问题,如果这时针对变量的操作是非原子的操作,这时如果只是简单的i++式的操作,可以使用原子类atomic类来保证操作的原子性(采用CAS实现),如果是复杂的业务操作,那么舍弃volatile,采用锁来解决并发问题(synchronized或者Lock)。

4、volatile总结

  volatile可以实现可见性和有序性,无法实现原子性 ,简单的原子操作可以委托给其他方式进行实现,复杂的原子操作需要借助锁来实现,这时候完全没有必要加上volatile了,因为锁已经包含了volatile的功能。  

  

java基础系列--volatile关键字的更多相关文章

  1. Java基础系列--static关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8477914.html 一.概述 static关键字是Java诸多关键字中较常使用的一个,从 ...

  2. Java基础系列--final关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8482909.html 一.概述 final是Java关键字中最常见之一,表示"最 ...

  3. Java基础系列--instanceof关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8492158.html instanceof关键字是在Java类中实现equals方法最常使 ...

  4. 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理

    目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 ...

  5. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

  6. 夯实Java基础系列9:深入理解Class类和Object类

    目录 Java中Class类及用法 Class类原理 如何获得一个Class类对象 使用Class类的对象来生成目标类的实例 Object类 类构造器public Object(); register ...

  7. Java基础系列-equals方法和hashCode方法

    原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述         equals方法和hashCode方法都是有Object类定义的. publi ...

  8. Java基础-标识符与关键字

    Java基础-标识符与关键字 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是标识符 标识符就是程序员在编写程序时,给类,变量,方法等起的名字. 二.标识符的命名规则 1& ...

  9. 夯实Java基础系列1:Java面向对象三大特性(基础篇)

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...

随机推荐

  1. freeType移植总结①——使用keil编译freeType2库

    在各个技术博客搜索相关资料后,终于将freeType的源码用keil工程编译通过,这里记录一下步骤和遇到的问题. 因为网上的资料都是旧版本freeType的工程,这里博主使用的是freeType2.9 ...

  2. JavaScript-BOM与DOM

    BOM与DOM BOM: Browser Object Model(浏览器对象模型),即把 浏览器 当做一个对象来看待.BOM 除了可以访问文档中的组件之外,还可以访问 浏览器组件,比如页面中的 na ...

  3. 模板基础model

    一.Django-model基础 1.1ORM 映射关系: 表名<---------->类名 字段<---------->属性 表记录<---------->类实例 ...

  4. What's the Difference Between Iterators and Generators in Python

    https://www.quora.com/Whats-the-difference-between-iterators-and-generators-in-Python

  5. iptables简单用法

    iptables -t 表名 <-A/I/D/R> 规则链名 [规则号] <-i/o 网卡名> -p 协议名 <-s 源IP/源子网> --sport 源端口 &l ...

  6. Eigen3安装及注意

    执行命令: sudo apt-get install libeigen3-dev 安装后执行以下命令 运行命令: sudo cp -r /usr/include/eigen3/Eigen /usr/i ...

  7. Delphi XE2 新增 System.Zip 单元, 可用一句话压缩整个文件夹了

    内主要就是 TZipFile 类, 最方便使用的是它的类方法: TZipFile.ExtractZipFile() //解压 Zip 文件到指定文件夹 TZipFile.IsValid() //判断指 ...

  8. 简单操作django中session和cookie

    cookie 1.会话技术 2.客户端的会话技术( 数据库保存在浏览器上) 3.问题导致原因: 在web应用中,一次网络请求是从request开始,到response结束,跟以后的请求或者跟其他请求没 ...

  9. C# WebSocket Fleck 调用非托管C++ DLL 实现通信(使用stringbuilder接收)

     [DllImport(@"XXX.dll", CallingConvention = CallingConvention.StdCall)]public static exter ...

  10. 转载:Package by feature, not layer

    原文地址:Package by feature, not layer Package by feature, not layer The first question in building an a ...