并发编程系列之深入理解volatile关键字

BitWanderlustPro
• 阅读 1564

  在学习volatile关键字之前先了解一下Java内存模型和happen-before原则。

Java内存模型

内存模型的特性

并发编程系列之深入理解volatile关键字
  线程1写:先写入本地内存,在同步到主内存。
  线程2读:先读本地内存,不能存在或失效在读主内存。
  这种内存结构是基于操作系统的逻辑虚拟出来的结构,并不是真实存在的,可以屏蔽各种硬件和操作系统的差异性,实现平台一致性。而且和jvm的运行时结构也没关联。主内存存储的是实例字段,静态字段和数据对象线程共享的数据。本地内存可以理解为主内存数据的拷贝。所有的线程只能操作本地内存,线程间的通信需要通过主内存。

原子性

  Java内存模型保证了read、load、use、assign、store、write、lock和unlock操作具有原子性,例如对一个int类型的变量执行assign赋值操作,这个操作就是原子性的。但是Java内存模型允许虚拟机将没有被volatile修饰的64位数据(long、double)的读写操作划分为两次32位的操作来进行,即load、store、read和write操作可以不具备原子性。

可见性

  可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。

并发编程系列之深入理解volatile关键字

有序性

  在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在Java内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,但是会影响到多线程并发执行的正确性。

happen-before原则

单一线程原则

  在一个线程内,在程序前面的操作先行发生于后面的操作。
  单线程内按代码顺序执行。但是,在不影响单线程环境执行结果的前提下,编译器和处理器可以进行重排序,这个是合法的。话句话说,这一规则无法保证编译重排和指令重排。
并发编程系列之深入理解volatile关键字

管程锁定规则

  一个unlock操作先行发生于后面对同一个锁的lock操作。

并发编程系列之深入理解volatile关键字

volatile变量规则

  对一个volatile变量的写操作先行发生于后面对这个变量的读操作。

并发编程系列之深入理解volatile关键字

线程启动规则

  Thread对象的start()方法调用先行发生于此线程的每一个动作。

并发编程系列之深入理解volatile关键字

线程加入规则

  Thread对象的结束先行发生于join()方法返回。

并发编程系列之深入理解volatile关键字

线程中断规则

  对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断时间的发生,可以通过interrupted()方法检测到是否有中断发生。

对象终结规则

  一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

传递性

  如果操作A先行发生于操作B,操作B先行发生于操作C,那么操作A先行发生于操作C。

volatile关键字

  volatile是一个类型修饰符。volatile的作用是作为指令关键字

volatile关键字的特性

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  • 禁止指令重排序。
  • volatile只能保证对单次读/写的原子性。

volatile关键字的可见性

  volatile的内存可见性是基于内存屏障实现的。
  在程序运行时,为了提高执行性能,编译器和处理器会对指令进行重排序,JMM为了保证在不同的编译器和CPU上有相同的结果,通过插入特定类型的内存屏障来禁止特定类型的编译器重排序和处理器重排序,插入一条内存屏障告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。

  • 1.lock前缀指令在多核处理器下会引发两件事情:
     1)将当前处理器缓存行的数据写回到系统内存
     2)写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效
  • 2.为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知何时会写到内存。
  • 3.为了保证各个处理器的缓存是一致的,实现了缓存一致性协议(MESI),每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作时,会重新从系统内存中把数据读到处理器缓存中。
  • 4.volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。

volatile关键字的有序性

volatile的happens-before关系

  对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

并发编程系列之深入理解volatile关键字

volatile禁止指令重排

  为了性能优化,JMM在不改变正确语义的前提下,会允许编译器和处理器对指令序列进行重排序。JMM提供了内存屏障阻止这种重排序。

  • 1.在每个volatile写操作的前面插入一个StoreStore屏障。
  • 2.在每个volatile写操作的后面插入一个StoreLoad屏障。
  • 3.在每个volatile读操作的后面插入一个LoadLoad屏障。
  • 4.在每个volatile读操作的后面插入一个LoadStore屏障。

  volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java并发编程,volatile内存实现和原理
前面的博文说了java的内存模型(https://my.oschina.net/90888/blog/1627359),介绍了java内存模型(https://my.oschina.net/90888/blog/1627359)的基础,此篇文章来说一下volatile关键字,这个在并发编程中,占有举足轻重地位的关键字。在java5.0之前它
红烧土豆泥 红烧土豆泥
4年前
(转载)Java内存区域(运行时数据区域)和内存模型(JMM) - czwbig
转载自:Java内存区域和内存模型是不一样的东西,内存区域是指Jvm运行时将数据分区域存储,强调对内存空间的划分。而内存模型(JavaMemoryModel,简称JMM)是定义了线程和主内存之间的抽象关系,即JMM定义了JVM在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java
Wesley13 Wesley13
3年前
Java多线程之volatile详解
目录:什么是volatile?JMM内存模型之可见性volatile三大特性之一:保证可见性volatile三大特性之二:不保证原子性volatile三大特性之三:禁止指令重排小结1.什么是volatile?答:volatile是java虚拟机提供的轻量级的同步机制(
Wesley13 Wesley13
3年前
Java 内存模型
什么是Java内存模型?JMM(JavaMemoryModel,Java内存模型),它定义了多线程访问Java内存的规范。简单的说有以下几部分内容:Java内存模型将内存分为主内存和工作内存定义了几个原子操作,用于操作主内存和工作内存中的变量定义了volatile变量的使用规则happensbefor
Wesley13 Wesley13
3年前
Java 深入理解volatile关键字
我们知道Java中volatile实现了修饰变量的原子性以及可见性,并且为了实现多线程环境下的线程安全,禁止了指令重排。首先我们先来了解一下happensbefore原则、asifserial语义以及数据依赖性,引用自《Java并发编程的艺术》happensbefore简介从JDK5开始,Java使用新的JSR133内存模型
Wesley13 Wesley13
3年前
Java并发编程:volatile关键字解析
volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java5之后,volatile关键字才得以重获生机。  volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关
Wesley13 Wesley13
3年前
Java面试官最爱的volatile关键字
在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度,而以volatile关键字作为一个小的切入点,往往可以一问到底,把Java内存模型(JMM),Java并发编程的一些特性都牵扯出来,深入地话还可以考察JVM底层实现以及操作系统的相关知识。下面我们以一次假想的面试过程,来深入了解下volitile关键字吧!面试官:
Wesley13 Wesley13
3年前
Java面试官最常问的volatile关键字
在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯出来,再深入的话还会考察JVM底层实现以及操作系统的相关知识。接下来让我们在一个假想的面试过程中来学习一下volitile关键字吧。1\.Java并发
Wesley13 Wesley13
3年前
Java多线程之内存可见性
Java多线程之内存可见性一、Java内存模型介绍什么是JMM?Java内存模型(JavaMemoryModel)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的细节所有的变量都存储在主内存中每个线程都
Wesley13 Wesley13
3年前
Java 多线程:volatile关键字
概念volatile也是多线程的解决方案之一。\\volatile能够保证可见性,但是不能保证原子性。\\它只能作用于变量,不能作用于方法。当一个变量被声明为volatile的时候,任何对该变量的读写都会绕过高速缓存,直接读取主内存的变量的值。如何理解直接读写主内存的值:回到多线程生成的原因(Java内存模型与
Wesley13 Wesley13
3年前
JAVA内存模型与线程以及volatile理解
Java内存模型是围绕在并发过程中如何处理原子性、可见性、有序性来建立的。一、主内存与工作内存  Java内存模型主要目标是在虚拟机中将变量存储到内存和从内存中取出变量。这里的变量包括:实例字段、静态字段、构成数组对象的元素;不包括局部变量和方法参数,因为它们是线程私有的。Java内存模型规定了所有变量都存储在主内存,线程的工作内