死磕Volatile

Rothschil 2020-05-20 09:09:00
Java

Volatile 关键字相对来说比较容易看的明白,但是在正式使用 Volatile之前,我们先熟悉下Java的内存模型,相信看完这个让你对如何判断哪些场景下使用 Volatile有一个基本认识。

1. 内存基础知识

1.1. JMM概念

JMM(Java Memory Model,简称JMM)并发设计采用的是共享内存的模型概念,所谓的共享内存模型,即它线程间的通信总是隐式进行,且整个的过程对外部来说不可见。

为了方便理解,我们比喻将每个线程都有自己私有的一块内存,称之为本地内存,本地内存中存储很多共享变量的副本,并都有对这些副本的 R/W

JMM内存模型

以上步骤存问题,如果在某一时刻,线程A、线程B需要对共享变量(i=0 进行 +1 操作)

小结,如果线程A 在对 共享变量 进行变更时,线程B 需要对修改后的变量内容,可以看见,这就是 Volatile 特点之一 可见性,此外它还有一个重要特性有序性
在并发编程领域中,一共有三个特性:原子性、可见性、有序性,而 Volatile 已经实现了两种特性。

1.2. 内存模型

JDK1.8内存模型

1.2.1. 程序计数器

线程私有的,互不影响、独立存储,是当前线程执行的字节码的行号指示器。让字节码解释器工作时可以通过改变这个计数器的值来选取下一执行的字节码的指令,分支、循环、跳转、异常、恢复都需要这个计数器来完成。

程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域。

1.2.2. 虚拟机栈

线程私有的,JAVA方法执行的动态内存模型,每个方法执行都会创建一个栈帧,伴随着方法创建到执行完成,用于存储局部变量表、操作数栈、动态链接、方法出口信息等组成。

1.2.3. 本地方法栈

虚拟机栈 类似,只不过它是虚拟机用到的 Native方法服务的,也有栈帧,同样的也会抛出与 虚拟机栈一样的异常。

1.2.4. 堆

这是虚拟机所管理的内存中最大的一块,所有对象实例均存储在这块区域,因此也被人称作GC堆,也是垃圾回收器重点工作的区域。分为:新生代、老年代。

1.2.5. 方法区(JDk1.7)

存储虚拟机加载类信息,常量,静态变量,即编译器编译后代码等数据

1.2.5.1. 方法区与永久代

1.2.5.2. 垃圾回收在方法区的行为

1.2.5.3. 异常的定义

1.3. 指令重排

指令重排是指在程序执行过程中, 为提高性能考虑,,编译器和CPU可能会对指令重新排序。

举个栗子:

1
2
3
4
1   int x = 10;
2 int y = 2;

3 int z = x + y;

理想情况下执行顺序应该是:1>2>3,但是经过JVM等优化侯,顺序会变成:2>1>3。

2. 什么是CAS?

在计算机科学中,实现多线程同步的原子指令特指比较和交换(Conmpare And Swap),它存在目的将内存中位置的内容与给定值进行比较。
只有在相同的情况下,将该内存位置的内容修改为新的给定值。这是作为单个原子操作完成的。 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败。 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成(摘自维基本科)

3. 内存模型