volatile变量自身具有下列特性。
1.可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写 入。 ·
2.原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不 具有原子性。
(PS:从JSR-133内存模型开始(即从JDK5开始),仅仅只允许把 一个64位long/double型变量的写操作拆分为两个32位的写操作来执行,任意的读操作在JSR133中都必须具有原子性(即任意读操作必须要在单个读事务中执行)。总结:64为的变量写操作可能会被拆分两个32位的写操作,不具备原子性,读操作是原子性)
volatile 单个变量的读写可看做使用了同一个锁对单个读写做了同步。
 
volatile写-读的内存语义(JMM为java内存模型):
有volatile变量修饰的共享变量进行写操作的时候被处理成汇编指令序列会多出Lock前缀的指令,Lock前缀的指令在多核处理器下会引发了两件事情。
1)将当前处理器缓存行的数据写回到系统内存。
2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内 ,,实质上是线程A向接下来将要读这个volatile变量的某个线程 发出了(其对共享变量所做修改的)消息。
实现原则:Lock前缀指令会引起处理器缓存回写到内存:使用“缓存锁 定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。
2.当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主 内存中读取共享变量。线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile 变量之前对共享变量所做修改的)消息。
实现原则:一个处理器的缓存回写到内存会导致其他处理器的缓存无效:处理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致 性。
(ps:线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过 主内存向线程B发送消息。)
 

volatile内存语义的实现

JMM会限制编译器重排序和处理器对volatile语义重排序。

volatile重排序规则表:

1.当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保 volatile写        之前的操作不会被编译器重排序到volatile写之后。

2.当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保 volatile读之后的操作不会被编译器重排序到volatile读之前。

3.当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。

为了实现volatile内存语义,编译器会在指令序列插入内存屏障来限制指令重排序。下面是基于保守策略的JMM内存屏障插入策略(关于内存屏障指令见文章末尾介绍):
1.在每个volatile写操作的前面插入一个StoreStore屏障。
2.在每个volatile写操作的后面插入一个StoreLoad屏障。
3.在每个volatile读操作的后面插入一个LoadLoad屏障。
4.在每个volatile读操作的后面插入一个LoadStore屏障。

volatile写插入内存屏障后生成的指令序列示意图:

这里比较有意思的是,volatile写后面的StoreLoad屏障。此屏障的作用是避免volatile写与 后面可能有的volatile读/写操作重排序。因为编译器常常无法准确判断在一个volatile写的后面 是否需要插入一个StoreLoad屏障(比如,一个volatile写之后方法立即return)。为了保证能正确 实现volatile的内存语义,JMM在采取了保守策略:在每个volatile写的后面,或者在每个volatile 读的前面插入一个StoreLoad屏障。从整体执行效率的角度考虑,JMM最终选择了在每个 volatile写的后面插入一个StoreLoad屏障。因为volatile写-读内存语义的常见使用模式是:一个 写线程写volatile变量,多个读线程读同一个volatile变量。当读线程的数量大大超过写线程时, 选择在volatile写之后插入StoreLoad屏障将带来可观的执行效率的提升。

volatile读插入内存屏障后生成的指令序列示意图:


上述volatile写和volatile读的内存屏障插入策略非常保守。在实际执行时,只要不改变 volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。下面通过具体的示例 代码进行说明。

针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化。

内存屏障类型表:

java关键字volatile内存语义详细分析的更多相关文章

  1. java多线程03-----------------volatile内存语义

    java多线程02-----------------volatile内存语义 volatile关键字是java虚拟机提供的最轻量级额的同步机制.由于volatile关键字与java内存模型相关,因此, ...

  2. java 关键字volatile

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  3. 深入汇编指令理解Java关键字volatile

    volatile是什么 volatile关键字是Java提供的一种轻量级同步机制.它能够保证可见性和有序性,但是不能保证原子性 可见性 对于volatile的可见性,先看看这段代码的执行 flag默认 ...

  4. JAVA关键字Volatile的特性

    一.简述: 关键字Volatile是JAVA虚拟机提供的最轻量级的同步机制,但是它并不容易完全被正确.完整的理解,以致于许多程序员在遇到需要处理多线程数据竞争的时候一律使用synchronized来进 ...

  5. volatile内存语义

    全面理解Java内存模型(JMM)及volatile关键字 volatile的内存语义 Volatile读写所建立的happens-before关系Volatile读写的内存语义 锁: 获取和释放Vo ...

  6. Java 关键字volatile的解释

    volatile 关键字特征: 1.可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的.可以禁止线程的工作内存对volatile修饰的变量进行缓存,并将修改的变量立即写入主存. 2. ...

  7. Java关键字-volatile

    关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制. 一旦某个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1.保证了不同线程对这个变 ...

  8. 内存屏障和volatile内存语义的实现

    趁周末,把以前的书拿出来,再翻一番,顺便做个笔记: 内存屏障:用来控制和规范cpu对内存操作的顺序的cpu指令. 内存屏障列表: 1.loadload:确保“前者数据装载”先于“后者装载指令”: 2. ...

  9. 关于 Java 关键字 volatile 的总结

    1 什么是 volatile volatile 是 Java 的一个关键字,它提供了一种轻量级的同步机制.相比于重量级锁 synchronized,volatile 更为轻量级,因为它不会引起线程上下 ...

随机推荐

  1. Android-ION内存管理简介

    ION内存管理简介 https://www.jianshu.com/p/4f681f6ddc3b http://kernel.meizu.com/memory%20management%20-%20i ...

  2. 《Python语言程序设计》【第3周】基本数据类型

    实例3:天天向上的力量 #DayDayUpQ1.py dayup = pow(1.001,365) daydown = pow(0.999,365) print("向上: {:.2f},向下 ...

  3. FZU ICPC 2020 寒假训练 2

    A - 排序 输入一行数字,如果我们把这行数字中的'5'都看成空格,那么就得到一行用空格分割的若 干非负整数(可能有些整数以'0'开头,这些头部的'0'应该被忽略掉,除非这个整数就是由 若干个'0'组 ...

  4. Python基础(range)

    arr = [1,2,3,4,5,6,7,8,9] for i in range(0,len(arr),2): print(arr[i],end=' | ') brr = arr[0:len(arr) ...

  5. Hadoop集群 增加节点/增加磁盘

    在虚拟机中新建一个机器. 设置静态IP 将修改/etc/hosts 192.168.102.10 master 192.168.102.11 slave-1 192.168.102.12 slave- ...

  6. 【Java】运行时Java对象在内存中是如何存储的?

    翻译自这一篇文章 我们知道函数在内存中实现为一个活动记录的栈.我们也知道Java方法在JVM栈区中实现为一个帧栈而Java对象是在堆区进行分配的. Java对象在堆内存中是怎样的呢?一旦对象保存在内存 ...

  7. 04373 C++程序设计 2019版 第一章习题五、程序设计题

    题目: 1.编写一个程序,将从键盘输入的n个字符串保存在一个一维数组A中.在输入字符串之前,先输入n的值.要求,数组A需要动态申请空间,程序运行结束前再释放掉. #include <iostre ...

  8. Java异常与错误

    Java错误与异常 三种类型的异常 检查性异常:用户错误或问题引起的异常,这是程序员无法预见的.例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略 运行时异常:运行时异常是 ...

  9. Python 3 快速入门 1 —— 数据类型与变量

    本文假设你已经有一门面向对象编程语言基础,如Java等,且希望快速了解并使用Python语言.本文对重点语法和数据结构以及用法进行详细说明,同时对一些难以理解的点进行了图解,以便大家快速入门.一些较偏 ...

  10. admixture 群体结构分析

    tructure是与PCA.进化树相似的方法,就是利用分子标记的基因型信息对一组样本进行分类,分子标记可以是SNP.indel.SSR.相比于PCA,进化树,群体结构分析可明确各个群之间是否存在交流及 ...