JAVA系列: 深入理解Volatile的实现原理

目录

Volatile

Volatile内存模型

Volatile的实现原理

Volatile的作用

demo例子


JAVA系列: 深入理解Volatile的实现原理

Volatile

[ˈvɒlətaɪl]

 

Volatile 是一个Java语言的类型修饰符。

一旦一个共享变量(类的成员变量、类的静态成员变量)被Volatile修饰之后,那么就具备了两层语义:

1、保证多线程下的可见性

2、禁止进行指令重排序(即保证有序性)

注意:Volatile只能让被他修饰内容具有可见性、有序性,并不能保证原子性

指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能

 

Volatile内存模型

JAVA系列: 深入理解Volatile的实现原理

多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存区域,而每个线程又单独的有自己的工作内存当线程与内存区域进行交互时数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数)。

线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存

JAVA系列: 深入理解Volatile的实现原理

Java语言规范指出:为了获得最佳速度,线程保存主存变量的私有拷贝,而且只当线程进入或者离开同步代码块时,才与主存变量的原始值对比。

Volatile的实现原理

缓存一致性协议( MESI协议)

导致可见性问题的根本原因是缓存以及重排序。 而 JMM 实际上就是提供了合理的禁用缓存以及禁止重排序的方法。所以它最核心的价值在于解决可见性和有序性。

 

JAVA系列: 深入理解Volatile的实现原理

主内存和工作内存之间的交互有具体的交互协议,JMM定义了八种操作来完成,这八种操作是原子的、不可再分的,它们分别是:lock,unlock,read,load,use,assign,store,write,其中lock,unlock,read,write作用于主内存;load,use,assign,store作用于工作内存。

(1) lock:将主内存中的变量锁定,为一个线程所独占

(2) unclock:将lock加的锁定解除,此时其它的线程可以有机会访问此变量

(3) read:将主内存中的变量值读到工作内存当中

(4) load:将read读取的值保存到工作内存中的变量副本中。

(5) use:将值传递给线程的代码执行引擎

(6) assign:将执行引擎处理返回的值重新赋值给变量副本

(7) store:将变量副本的值存储到主内存中。

(8) write:将store存储的值写入到主内存的共享变量当中。

  • 从主存复制变量到当前工作内存(read and load)
  • 执行代码,改变共享变量值 (use and assign)
  • 用工作内存数据刷新主存相关内容 (store and write)

指令规则

  • read 和 load、store和write必须成对出现
  • assign操作,工作内存变量改变后必须刷回主内存
  • 同一时间只能运行一个线程对变量进行lock,当前线程lock可重入,unlock次数必须等于lock的次数,该变量才能解锁。
  • 对一个变量lock后,会清空该线程工作内存变量的值,重新执行load或者assign操作初始化工作内存中变量的值。
  • unlock前,必须将变量同步到主内存(store/write操作)

 

Volatile的作用

JAVA系列: 深入理解Volatile的实现原理                JAVA系列: 深入理解Volatile的实现原理

 

强制从主存中取得变量的值,而不是从线程工作空间中取得变量的值,从而使变量在多个线程间可见

由于volatile屏蔽掉了JVM的代码优化,所以在效率上比较低,因此一定在必要时才使用。

 

demo例子

Volatile例子