要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸。最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍。

有什么用?

volatile主要对所修饰的变量提供两个功能

  • 可见性
  • 防止指令重排序

本篇博客主要对volatile可见性进行探讨,以后发表关于指令重排序的博文。

什么是可见性?

一图胜千言

 

上图已经把JAVA内存模型(JMM)展示得很详细了,简单概括一下

  1. 每个Thread有一个属于自己的工作内存(可以理解为每个厨师有一个属于自己的铁锅)
  2. 所有Thread共用一个主内存(餐厅所有的厨师共用同一个冰箱)
  3. 每个Thread操作数据之前都会去主内存中获取数据(厨师炒菜之前都要去冰箱里拿食材)
  • Thread:厨师
  • 工作内存:铁锅
  • store&load:放熟食,取食材
  • 主内存:冰箱

读者可思考以下情景:

餐厅来了一位顾客点了一份红烧肉,此时有两位大厨(假设大厨之间互不通信),由于互不通信,所以两位大厨都打开冰箱取出食材开始炒菜。 最后炒出了两份红烧肉,顾客只要一份。为什么会造成这种结果?

由于大厨之间没有可见性。

将此情景放在JAVA中即是:

线程A从主内存中取了一个变量到工作内存中,操作完毕后没有及时放回主内存中,于是线程B去取这个变量已经 过期 了,取的是线程A操作之前的变量。

如何拥有可见性?

先介绍一下Java内存模型中定义的8种工作内存与主内存之间的原子操作

  • lock( 锁定 ):作用于 主内存的变量 ,把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于 主内存 的变量,把一个处于锁定的变量释放出来,释放变量才可以被其他线程锁定。
  • read(读取):作用于 主内存 的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于***工作内存***的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于***工作内***存种的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于***工作内存***中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于***工作内存***的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用
  • write(写入):作用于 主内存 的变量,它把store操作从工作内存中得到的值放入主内存的变量中。

读取赋值一个普通变量的情况

 

当线程1对主内存对象发起read操作到write操作套流程的时间里,线程2随时都有可能对这个主内存对象发起第二套操作

  • 有什么危害呢?

假设主内存中有一个

int a=0;

线程1和线程2分别执行一次,理想状态下最终a的值为2.

a++;

线程1在执行了assign操作之后变量a的真实值已经从0变成了1,但是这个过程发生在工作内存中对其他线程不可见,若线程2此时对变量a的操作,读取到的值仍然为0,因为没有可见性,线程2的操作也仅仅是重复了线程1的操作,再次让a从0变成了1。 并没有达到期望的a=2。

读取赋值一个volatile变量的情况

 volatile变量对对象的操作更严格:
  • use之前不能被read&load
  • assign之后必须紧跟store&write

也就是说 read-load-use 和 assign-store-write 成为了两个不可分割的原子操作

尽管这时候在use和assign之间依然有一段真空期,有可能变量会被其他线程读取,但是无论在哪一个时间点主内存的变量和任一工作内存的变量的值都是相等的。这个特性就导致了volatile变量不适合参与到依赖当前值的运算,如自增。 那么依靠可见性的特点volatile可以用在哪些地方呢? 《Java虚拟机》提到:

运算结果并不依赖变量的当前值(即结果对产生中间结果不依赖),或者能够确保只有单一的线程修改变量的值

通常volatile用做 保存某个状态 的boolean值。

Java并发编程-volatile可见性的介绍的更多相关文章

  1. Java并发编程 Volatile关键字解析

    volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...

  2. Java 并发编程——volatile与synchronized

    一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...

  3. Java并发编程volatile关键字

    volatile理解 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制.volatile具有synchronized关键字的“可见性”,vo ...

  4. Java并发编程--Volatile详解

    摘要      Volatile是Java提供的一种弱同步机制,当一个变量被声明成volatile类型后编译器不会将该变量的操作与其他内存操作进行重排序.在某些场景下使用volatile代替锁可以减少 ...

  5. Java并发编程-线程可见性&线程封闭&指令重排序

    一.指令重排序 例子如下: public class Visibility1 { public static boolean ready; public static int number; } pu ...

  6. java并发编程 volatile关键字 精准理解

    1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...

  7. Java并发编程-volatile

    一. volatite 简述Java 语言提供了一种稍弱的同步机制,即 volatile 变量.用来确保将变量的更新操作通知到其他线程,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新. ...

  8. java并发编程[持续更新]

    目录 java并发编程 1.常用类介绍 Semaphore 2.名词解释 2.1 线程安全 2.2 可重入锁和不可重入锁 java并发编程 1.常用类介绍 Semaphore Semaphore 类是 ...

  9. [Java并发编程(三)] Java volatile 关键字介绍

    [Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...

随机推荐

  1. js数据结构与算法--单链表的实现与应用思考

    链表是动态的数据结构,它的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 现实中,有一些链表的例子. 第一个就是寻宝的游戏.你有一条线索,这条线索是指向寻找下一条线 ...

  2. c/c++再学习:常用字符串转数字操作

    c/c++再学习:常用字符串转数字操作 能实现字符串转数字有三种方法,atof函数,sscanf函数和stringstream类. 具体demo代码和运行结果 #include "stdio ...

  3. 关于snappy的压缩 hadoop和hbase均支持

    临时做个随笔 下载snappy-1.1.1.tar.gz 解压后需要提前安装: yum -y update gccyum -y install gcc+ gcc-c++ =============== ...

  4. Schedule Problem spfa 差分约束

    题意:有n个任务,给出完成n个任务所需时间,以及一些任务安排.任务安排有四种: FAS a b:任务a需在任务b开始后完成. FAF a b:任务a需在任务b完成后完成. SAF a b:任务a需在任 ...

  5. spring boot 入门之 helloworld

    第一步:创建一个普通的maven项目 第二步:配置springboot基础依赖配置(即配置pom和启动类) POM配置 <project xmlns="http://maven.apa ...

  6. kaggle之泰坦尼克号乘客死亡预测

    目录 前言 相关性分析 数据 数据特点 相关性分析 数据预处理 预测模型 Logistic回归训练模型 模型优化 前言 一般接触kaggle的入门题,已知部分乘客的年龄性别船舱等信息,预测其存活情况, ...

  7. Dockerfile中COPY命令的简单性

    dockerfile中的COPY命令是不会拷贝目录结构的,它只会单纯把包含的所有文件拷贝到另一个目录中去. 相关链接:https://www.cnblogs.com/sparkdev/p/957324 ...

  8. Hibernate HQL ②

    分页查询: - setFirstResult(int firstResult):设定从哪一个对象开始检索,参数 firstResult 表示这个对象在查询结果中的索引位置,索引位置的起始值为零.默认情 ...

  9. Nginx的gzip

    webpack  compression-webpack-plugin => .gz CompressionPlugin = require("compression-webpack- ...

  10. WdatePicker 日期控件- 功能及示例

      3. 多语言和自定义皮肤多语言支持 通过lang属性,可以为每个日期控件单独配置语言,当然也可以通过WdatePicker.js配置全局的语言语言列表和语言安装说明详见语言配置 示例3-1 多语言 ...