【并发编程】volatile实现原理解析

news2025/9/20 17:20:40

📫作者简介:小明Java问道之路2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。

           

🏆 2022博客之星TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人

🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家

         

 🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~ 


🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

专栏系列(点击解锁)

学习路线(点击解锁)

知识定位

🔥Redis从入门到精通与实战🔥

Redis从入门到精通与实战

围绕原理源码讲解Redis面试知识点与实战

🔥MySQL从入门到精通🔥

MySQL从入门到精通

全面讲解MySQL知识与企业级MySQL实战

🔥计算机底层原理🔥

深入理解计算机系统CSAPP

以深入理解计算机系统为基石,构件计算机体系和计算机思维

Linux内核源码解析

围绕Linux内核讲解计算机底层原理与并发

🔥数据结构与企业题库精讲🔥

数据结构与企业题库精讲

结合工作经验深入浅出,适合各层次,笔试面试算法题精讲

🔥互联网架构分析与实战🔥

企业系统架构分析实践与落地

行业最前沿视角,专注于技术架构升级路线、架构实践

互联网企业防资损实践

互联网金融公司的防资损方法论、代码与实践

🔥Java全栈白宝书🔥

精通Java8与函数式编程

本专栏以实战为基础,逐步深入Java8以及未来的编程模式

深入理解JVM

详细介绍内存区域、字节码、方法底层,类加载和GC等知识

深入理解高并发编程

深入Liunx内核、汇编、C++全方位理解并发编程

Spring源码分析

Spring核心七IOC/AOP等源码分析

MyBatis源码分析

MyBatis核心源码分析

Java核心技术

只讲Java核心技术

本文目录

本文导读

一、什么是volatile

二、volatile 的使用场景

三、什么是 happens-before 关系

四、volatile 的原理

1、可见性(lock 前缀的指令)

2、禁止重排(内存屏障)

五、volatile 和 synchronized 的关系

六、单例双重检查锁模式中为什么要 volatile

总结


本文导读

本文深入浅出讲解了什么是volatile与volatile 的使用场景,对什么是 happens-before 关系进行不同场景分析,最后分析volatile 可见性(lock指令)与禁止重排(内存屏障)的原理,最后给出两道常见面试题“volatile 和 synchronized 的关系”、“单例双重检查锁模式中为什么要 volatile”作为本文扩展。

一、什么是volatile

volatile 是轻量级的 synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是 volatile 修饰的变量对所有线程总数立即可见的,对volatile变量的所有写操作总是能立刻反应到其他线程中。

好处:比 synchronized 的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。

volatile 保证可见性和禁止重排序,无法保证原子性。Java 提供 volatile 来保证一定的有序性。最著名的例子就是单例模式里面的双重检查锁。重排序它不会影响单线程的运行结果,但是对多线程会有影响。

二、volatile 的使用场景

通常情况,volatile 用来修饰 boolean 类型的标记位,因为对于标记位来讲,直接的赋值操作本身就是具备原子性的,再加上 volatile 保证了可见性,那么就是线程安全的了。

对于多个线程同时操作的的场景,不仅仅是一个简单的赋值操作,而是需要先读取当前的值,然后在此基础上进行一定的修改,再把它给赋值回去。这样一来,我们的 volatile 就不足以保证这种情况的线程安全了。

三、什么是 happens-before 关系

happens-before:该原则保证了程序的“有序性”,它规定如果两个操作的执行顺序无法从 happens-before 原则中推到出来,那么他们就不能保证有序性,可以随意进行重排序。

也就是说,一个线程对于变量的操作需要对另一线程对于变量的操作可见,一定存在happens-before 关系

· 程序顺序原则:即在一个线程内必须保证语义串行性,也就是说按照代码顺序执行。

· 锁规则:解锁(unlock)操作必然发生在后续的同一个锁的加锁(lock)之前,也就是说,如果对于一个锁解锁后,再加锁,那么加锁的动作必须在解锁动作之后(同一个锁)。

· volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性,简单的理解就是,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存,任何时刻,不同的线程总是能够看到该变量的最新值。

· 线程启动规则:线程的start()方法先于它的每一个动作,即如果线程A在执行线程B的start方法之前修改了共享变量的值,那么当线程B执行start方法时,线程A对共享变量的修改对线程B可见
传递性 A先于B ,B先于C 那么A必然先于C

· 线程终止规则:线程的所有操作先于线程的终结,Thread.join()方法的作用是等待当前执行的线程终止。假设在线程B终止之前,修改了共享变量,线程A从线程B的 join方法成功返回后,线程B对共享变量的修改将对线程A可见。

· 线程中断规则:对线程 interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测线程是否中断。

· 对象终结规则对象的构造函数执行,结束先于finalize()方法

四、volatile 的原理

1、可见性(lock 前缀的指令)

第一层的作用是保证可见性。Happens-before 关系中对于 volatile 是这样描述的:对一个 volatile 变量的写操作 happen-before 后面对该变量的读操作。

这就代表了如果变量被 volatile 修饰,那么每次修改之后,接下来在读取这个变量的时候一定能读取到该变量最新的值。

内存屏障(Memory Barrier)的另外一个作用是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。

对 Volatile 进行写操作 CPU 会在,有 volatile 变量修饰的共享变量进行写操作的时候,多lock汇编代码。

lock 前缀的指令在CPU中会引发了两件事情:一、将当前处理器缓存行的数据会写回到系统内存。二、这个写回内存的操作会引起在其他 CPU 里缓存了该内存地址的数据无效。

2、禁止重排(内存屏障)

第二层的作用就是禁止重排序。先介绍一下 as-if-serial语义:不管怎么重排序,(单线程)程序的执行结果不会改变。在满足 as-if-serial 语义的前提下,由于编译器或 CPU 的优化,代码的实际执行顺序可能与我们编写的顺序是不同的,这在单线程的情况下是没问题的,但是一旦引入多线程,这种乱序就可能会导致严重的线程安全问题。

用了 volatile 关键字就可以在一定程度上禁止这种重排序

volatile变量正是通过内存屏障实现其在内存中的语义,即可见性和禁止重排优化:

1、在每个volatile写操作的前面插入一个StoreStore,使对于变量写操作的后面的写操作不会重排序到前面

2、在每个volatile写操作的后面插入一个StoreLoad,使对于变量读操作的后面的写操作不会重排序到前面

3、在每个volatile读操作的后面插入一个LoadLoad,使对于变量读操作的后面的读操作不会重排序到前面

4、在每个volatile读操作的后面插入一个LoadStore,使对于变量写操作的后面的读操作不会重排序到前面

五、volatile 和 synchronized 的关系

相似性:volatile 可以看作是一个轻量版的 synchronized,比如一个共享变量如果自始至终只被各个线程赋值和读取,而没有其他操作的话,那么就可以用 volatile 来代替 synchronized 或者代替原子变量,足以保证线程安全。

实际上,对 volatile 字段的每次读取或写入都类似于“半同步”——读取 volatile 与获取 synchronized 锁有相同的内存语义,而写入 volatile 与释放 synchronized 锁具有相同的语义。

性能方面:volatile 属性的读写操作都是无锁的,正是因为无锁,所以不需要花费时间在获取锁和释放锁上,所以比 synchronized 性能更好。

六、单例双重检查锁模式中为什么要 volatile

public class Singleton {
    private static volatile Singleton singleton;

    public static Singleton getInstance() {

        if (singleton == null) {
            synchronized (Singleton.class) {

                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

主要就在于 singleton = new Singleton() ,它并非是一个原子操作,在 JVM 中上述语句至少做了以下这 3 件事,因为存在指令重排序的优化,也就是说第2 步和第 3 步的顺序是不能保证的。

总结

本文深入浅出讲解了什么是volatile与volatile 的使用场景,对什么是 happens-before 关系进行不同场景分析,最后分析volatile 可见性(lock指令)与禁止重排(内存屏障)的原理,最后给出两道常见面试题“volatile 和 synchronized 的关系”、“单例双重检查锁模式中为什么要 volatile”作为本文扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1278095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最小生成树算法

文章目录 最小生成树概述 P r i m Prim Prim 算法 - 稠密图 - O ( n 2 ) O(n^2) O(n2)思路概述时间复杂度分析AcWing 858. Prim算法求最小生成树CODE K r u s k a l Kruskal Kruskal 算法 - 稀疏图 - O ( m l o g m ) O(mlogm) O(mlogm)思路解析时间复杂度分析AcWing 859. Kr…

Java数据结构之《哈希查找》题目

一、前言: 这是怀化学院的:Java数据结构中的一道难度中等的一道编程题(此方法为博主自己研究,问题基本解决,若有bug欢迎下方评论提出意见,我会第一时间改进代码,谢谢!) 后面其他编程题只要我写完…

7.C转python

1.对字典的各种操作都是对键来进行的 2.关于字典的遍历操作 例: 还可以这样遍历 所以生成了一个固定模版来遍历字典: 例: 那两个名字可以换 例: 3.合法key的类型: 要求可哈希 在python中,专门提供了一个hash()函数来计算哈希值 例: 有的类型是不能计算哈希的,如:列表,字…

YOLOv8界面-目标检测+语义分割+追踪+姿态识别(姿态估计)+界面DeepSort/ByteTrack-PyQt-GUI

YOLOv8-DeepSort/ByteTrack-PyQt-GUI:全面解决方案,涵盖目标检测、跟踪和人体姿态估计 YOLOv8-DeepSort/ByteTrack-PyQt-GUI是一个多功能图形用户界面,旨在充分发挥YOLOv8在目标检测/跟踪和人体姿态估计/跟踪方面的能力,与图像、…

腾讯云云服务器功能与优势

腾讯云云服务器(Cloud Virtual Machine,CVM)是腾讯云提供的可扩展的计算服务。使用云服务器 CVM 避免了使用传统服务器时需要预估资源用量及前期投入的问题,帮助您在短时间内快速启动任意数量的云服务器并及时部署应用程序。 云服…

在 AlmaLinux9 上安装Oracle Database 23c

在 AlmaLinux9 上安装Oracle Database 23c 0. 下载 Oracle Database 23c 安装文件1. 安装 Oracle Database 23c3. 连接 Oracle Database 23c4. (谨慎)卸载 Oracle Database 23c 0. 下载 Oracle Database 23c 安装文件 版权问题,下载地址请等待…

ASP.NET版本WOL服务的使用

本文以WOL为例,演示如何通过 GPT-4 让其为 WebAPI 项目设计一个网页。其中介绍了如何让GPT4生成相关功能,添加动画效果,接口鉴权等。 1. 背景 前面我们已经完成了一个WOL服务的开发,并将其迁移改造为了 ASP.NET 服务并完成了部署…

电脑提示mfc100u.dll缺失如何解决?分享有效的5个解决方法

由于各种原因,电脑可能会出现一些问题,其中之一就是电脑提示mfc100u.dll的错误。这个问题可能会导致电脑无法正常运行某些程序或功能。为了解决这个问题,我将分享验证有效的五个修复方法,帮助大家恢复电脑的正常运行。 首先&#…

【spring(六)】WebSocket网络传输协议

🌈键盘敲烂,年薪30万🌈 目录 核心概要: 概念介绍: 对比HTTP协议:⭐ WebSocket入门案例:⭐ 核心概要: websocket对比http 概念介绍: WebSocket是Web服务器的一个组件…

一、服务器准备

本案例使用VMware Workstation Pro虚拟机创建虚拟服务器来搭建Linux服务器集群,所用软件及版本如下: Centos7.7-64bit 1、三台虚拟机创建 第一种方式:通过iso镜像文件来进行安装(不推荐) 第二种方式:直接复制安装好的虚拟机文…

CAP BASE理论

CAP & BASE理论详解 CAP 理论 简介 CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。 CAP 理论的提出者布鲁尔在提出 CAP 猜想的时…

LZW的编码和解码

不同于哈弗曼编码针对于每个元素编码,LZW主要针对字符串的编码优化,也就是把出现频率高的字符串压缩成一个字符表示,这也是大名鼎鼎的GIF采用的压缩格式。下面我将从三个角度谈谈我的一些理解,文章主要参考了这位大佬:…

灯光开不了了,是不是NVIDIA的问题

如果你跟我一样灯光亮度调节不了了,然后显示适配器又没有了,你看一下是不是和我这个大怨种一样把NVIDIA卸了,为了这个东西,这屏幕亮瞎我的眼镜😢😢。只需要进入官网,你就可以直接找到&#xff0…

浅析SD-WAN企业组网部署中简化网络运维的关键技术

网络已经成为现代企业不可或缺的基础设施,它为企业提供了连接全球的桥梁。随着全球化和数字化转型的加速推进,企业面临着越来越多的网络挑战和压力。传统的网络组网方式往往无法满足企业规模扩大、分支机构增多、上云服务等需求,导致网络性能…

005、简单页面-容器组件

之——布局 目录 之——布局 杂谈 正文 1.布局基础知识 2.Column 3.Row 4.实践 杂谈 布局容器组件。 一个丰富的页面需要很多组件组成,那么,我们如何才能让这些组件有条不紊地在页面上布局呢?这就需要借助容器组件来实现。 容器组件是…

深入理解Servlet(中)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 上篇有一张图&#xff…

Istio可观测性

Istio可观测性 image-20231129072302901 前言 Istio 为网格内所有的服务通信生成详细的遥测数据。这种遥测技术提供了服务行为的可观测性,使运维人员能够排查故障、维护和优化应用程序,而不会给开发人员带来其他额外的负担。通过 Istio,运维…

(C语言)找出1-99之间的全部同构数

同构数&#xff1a;它出现在平方数的右边。例&#xff1a;5是25右边的数&#xff0c;25是625右边的数&#xff0c;即5和25均是同构数。 #include<stdio.h> int main() {for(int i 1;i < 100;i ){if((i*i % 10 i) || (i*i % 100 i))printf("%d\t%d\n",i,…

随堂复习(异常处理、多线程)

第09章&#xff1a;随堂复习&#xff08;异常处理&#xff09; 一、随堂复习 1. 异常的概述 1. 什么是异常&#xff1f; 指的是程序在执行过程中&#xff0c;出现的非正常情况&#xff0c;如果不处理最终会导致JVM的非正常停止。2. 异常的抛出机制 Java中把不同的异常用不同的…

【文献阅读】Joint Demosaicing and Denoising with Self Guidance

1. 摘要 近年来&#xff0c;一些神经网络在联合去马赛克和去噪(JDD)方面表现出了良好的效果。大多数算法首先将Bayer原始图像分解为四通道RGGB图像&#xff0c;然后将其输入神经网络。这种做法忽略了一个事实&#xff0c;即绿色通道的采样率是红色和蓝色通道的两倍。在本文中&…