Java并发编程-volatile可见性的介绍

前言

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

有什么用?

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

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


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

什么是可见性?

一图胜千言 学习

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

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

读者可思考如下情景:
餐厅来了一位顾客点了一份红烧肉,此时有两位大厨(假设大厨之间互不通讯),因为互不通讯,因此两位大厨都打开冰箱取出食材开始炒菜。 最后炒出了两份红烧肉,顾客只要一份。为何会形成这种结果?spa

因为大厨之间没有可见性。

将此情景放在JAVA中便是:
线程A从主内存中取了一个变量到工做内存中,操做完毕后没有及时放回主内存中,因而线程B去取这个变量已经过时了,取的是线程A操做以前的变量。线程

如何拥有可见性?

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

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

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

  • 有什么危害呢?

假设主内存中有一个code

int a=0;
复制代码

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

a++;
复制代码

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

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

volatile变量
volatile变量对对象的操做更严格:

  • use以前不能被read&load
  • assign以后必须紧跟store&write

也就是说 read-load-useassign-store-write成为了两个不可分割的原子操做

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

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

一般volatile用作保存某个状态的boolean值。


部分参考自

相关文章
相关标签/搜索