(java关键字含义)(java关键字是指什么)

虽然平时用volatile关键字不是很多,但volatile作为Java多线程开发中最轻量级的同步机制,也是面试中经常问到的问题。

volatile关键词作用于变量,具有两种语义:一是保证变量对所有线程的可见性,二是禁止进行指令重排序。下面分别加以说明。

线程可见性

用volatile关键字修饰变量,就相当于告诉编译器:这个变量是易变的(mutable)。当一条线程修改了 volatile 变量的值,新值对于其他线程来说是可以立即得知的。

前文《 》介绍过Java内存模型,线程之间共享数据必须要通过主内存。volatile的作用是确保每次对volatile变量的读操作都从主内存里读取变量的值,每次对volatile变量的写操作也都将值写到主内存里。

(java关键字含义)(java关键字是指什么)

Java内存模型

线程写 volatile 变量的过程:

  1. 改变线程工作内存中 volatile 变量副本的值
  2. 将改变后的副本的值从工作内存刷新到主内存

线程读 volatile 变量的过程:

  1. 从主内存中读取 volatile 变量的最新值到线程的工作内存中
  2. 从工作内存中读取 volatile 变量的副本

禁止重排序

编译器有时候会对指令进行重排序优化以获得更好的性能,但有时候可能没有意识到这种优化可能会导致程序没有产生预期行为。为了确保程序产生预期的行为,需要阻止编译器对一些变量的读写指令做优化,可以使用volatile关键字修饰变量。

volatile禁止重排序的规则如下:

  • 当程序执行到 volatile 变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果对后面的操作可见;在其后面的操作肯定还没有进行;
  • 在进行指令优化时,不能将后面的代码放到volatile变量访问语句之前执行,也不能将前面的代码放到volatile变量访问语句之后执行。

volatile 实现禁止重排序的原理

JMM的处理器重排序规则会要求Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers)指令,通过内存屏障来禁止特定类型的处理器重排序。

内存屏障类型包括下表列出的四种。

(java关键字含义)(java关键字是指什么)

内存屏障类型 《java并发编程的艺术》

volatile实现禁止重排序的方式就是在编译生成字节码时,在对volatile变量的读写操作前后增加了内存屏障,以阻止和其他指令的执行顺序被重排。

volatile 写操作

(java关键字含义)(java关键字是指什么)

volatile写操作的内存屏障

  • 在每个 volatile 写操作的前面会插入一个 StoreStore 屏障。 该屏障保证了volatile 写操作不会和之前的写操作发生重排序。
  • 在每个 volatile 写操作的后面会插入一个 StoreLoad 屏障。 该屏障保证了 volatile 写操作不会与之后的volatile读/写操作重排序。

volatile 读操作

(java关键字含义)(java关键字是指什么)

volatile读操作的内存屏障

  • 在每个 volatile 读操作的后面会插入一个 LoadLoad 屏障。 该屏障保证了 volatile 读操作不会与之后的读操作发生重排序。
  • 在每个 volatile 读操作的后面还会插入一个 LoadStore 屏障。 该屏障保证了 volatile 读操作不会与之后的写操作进行重排序。
(java关键字含义)(java关键字是指什么)

JMM针对编译器制定的volatile重排序规则表(Doug Lea)

上面说的要点绕,我们再来总结一下:

  • 只要第二个操作是volatile写,不管第一个操作是什么都不会重排序
  • 只要第一个操作是volatile读,不管第二个操作是什么都不会重排序
  • 第一个操作是volatile写,第二个操作是volatile读,也不会发生重排序

volatile不保证原子性

前面说了volatile可以保证可见性和有序性,但volatile不能保证操作是原子的。比如下面的例子,多线程运行下,自增函数 inc() 不是原子的:

(java关键字含义)(java关键字是指什么)

count虽是volatile变量,但多线程并发执行时,无法保证inc()的原子性

count ++ 相当于count = count + 1,其实可以分解为好几步:

  1. 先读取 count 的值到 tmp
  2. 增加 tmp 的值
  3. 把 tmp 的值写回到 count 的地址里

上述三步操作每一步都是原子的,但是三步合起来却不是原子的。若要确保 inc() 的原子性,需要采用 synchronized 或 CAS 等其他方法解决。

volatile 的使用场景总结起来,就是“一次写入,到处读取”,某一线程负责更新变量,其他线程读取变量。像刚才的例子,有多个线程同时对volatile变量进行写入,就不适合用了。

我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法,欢迎大家关注,谢谢。

声明:我要去上班所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,版权归原作者微说互联网所有,原文出处。若您的权利被侵害,请联系删除。

本文标题:(java关键字含义)(java关键字是指什么)
本文链接:https://www.51qsb.cn/article/m8dle.html

(0)
打赏微信扫一扫微信扫一扫QQ扫一扫QQ扫一扫
上一篇2022-12-29
下一篇2022-12-29

你可能还想知道

发表回复

登录后才能评论