【并发编程】volatile原理

 

       📝个人主页:五敷有你      
 🔥系列专栏:并发编程
⛺️稳重求进,晒太阳

volatile原理实现是内存屏障,Memory Barrier

  • 对volatile变量的写指令会加入写屏障。
  • 对volatile变量的读指令会加入读屏障

如何保证可见性

写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中

不只是把volatile修改同步到主存中,还会把之前的一些修改同步到主存中

如下,就是在同步ready的时候也会同步num

public void actor2(I_Result r) {
        num = 2;//之后也会同步到主存中
        ready = true; // ready 是 volatile 赋值带写屏障
        // 写屏障
        }

而读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据

如下,就是从主存中读取到的值ready

public void actor1(I_Result r) {
      // 读屏障
      // ready 是 volatile 读取值带读屏障
        if(ready) {
        r.r1 = num + num;
        } else {
        r.r1 = 1;
      }
     }

下图理解 

如何保证有序性

1.写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后

如下:就是防止num的赋值操作走到ready=true的后面(防止前面的之前的语句没做,影响后续的结果)

public void actor2(I_Result r) {
        num = 2;
        ready = true; // ready 是 volatile 赋值带写屏障
        // 写屏障
        }

2.读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前

如下:读屏障是防止后面的赋值操作走到前面(走到前面的话会有概率影响你的volatile修饰的变量)

public void actor1(I_Result r) {
        // 读屏障
        // ready 是 volatile 读取值带读屏障
        if(ready) {
        r.r1 = num + num;
     } else {
        r.r1 = 1;
    }
   }

不能解决指令交错

  • 写屏障仅仅是保证之后的读能够读到最新的结果,但不能保证读跑到它前面去(不懂)。t2线程啥时候读不能保证
  • 而有序性的保证也只是保证了本线程内相关代码不被重排序。至于两个线程之间谁前谁后,这是由CPU决定的

double-checked locking问题

synchronized是可以保证原子有序,可见的,但前提是,需要把共享变量都交给synchronized管理

下面的代码就有外部暴漏的风险

public final class Singleton {
    private Singleton() { }
    private static Singleton INSTANCE = null;
    public static Singleton getInstance() {
        if(INSTANCE == null) { // t2
            // 首次访问会同步,而之后的使用没有 synchronized
            synchronized(Singleton.class) {
                if (INSTANCE == null) { // t1
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

以上的实现特点是:

  • 懒惰实例化
  • 首次使用 getInstance() 才使用 synchronized 加锁,后续使用时无需加锁
  • 有隐含的,但很关键的一点:第一个 if 使用了 INSTANCE 变量,是在同步块之外

但在多线程环境下,上面的代码是有问题的,getInstance 方法对应的字节码为:

其中

17 表示创建对象,将对象引用入栈 // new Singleton

20 表示复制一份对象引用 // 引用地址

21 表示利用一个对象引用,调用构造方法

24 表示利用一个对象引用,赋值给 static INSTANCE

也许 jvm 会优化为:先执行 24,再执行 21。如果两个线程 t1,t2 按如下时间序列执行:

        关键在于 0: getstatic 这行代码在 monitor 控制之外,它就像之前举例中不守规则的人,可以越过 monitor 读取INSTANCE 变量的值

        这时 t1 还未完全将构造方法执行完毕,如果在构造方法中要执行很多初始化操作,那么 t2 拿到的是将是一个未初始化完毕的单例

        对 INSTANCE 使用 volatile 修饰即可,可以禁用指令重排(防止synchronized的部分指令跑到instance的前面去),但要注意在 JDK 5 以上的版本的 volatile 才会真正有效

double-checked locking 解决

public final class Singleton {
    private Singleton() { }
    private static volatile Singleton INSTANCE = null;
    public static Singleton getInstance() {
        // 实例没创建,才会进入内部的 synchronized代码块
        if (INSTANCE == null) {
            synchronized (Singleton.class) { // t2
                // 也许有其它线程已经创建实例,所以再判断一次
                if (INSTANCE == null) { // t1
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

字节码上看不出来 volatile 指令的效果

// -------------------------------------> 加入对 INSTANCE 变量的读屏障

0: getstatic #2               // Field INSTANCE:Lcn/itcast/n5/Singleton;

3: ifnonnull 37

6: ldc #3 // class cn/itcast/n5/Singleton

8: dup

9: astore_0

10: monitorenter -----------------------> 保证原子性、可见性

11: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;

14: ifnonnull 27

17: new #3 // class cn/itcast/n5/Singleton

20: dup

21: invokespecial #4 // Method "":()V

24: putstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;

// -------------------------------------> 加入对 INSTANCE 变量的写屏障

27: aload_0

28: monitorexit ------------------------> 保证原子性、可见性

29: goto 37

32: astore_1

33: aload_0

34: monitorexit

35: aload_1

36: athrow

37: getstatic #2 // Field INSTANCE:Lcn/itcast/n5/Singleton;

        如上面的注释内容所示,读写 volatile 变量时会加入内存屏障(Memory Barrier(Memory Fence)),保证下面两点:

  • 可见性:
    • 写屏障(sfence)保证在该屏障之前的 t1 对共享变量的改动,都同步到主存当中
    • 而读屏障(lfence)保证在该屏障之后 t2 对共享变量的读取,加载的是主存中最新数据
  • 有序性
    • 写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
    • 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
  • 更底层是读写变量时使用 lock 指令来多核 CPU 之间的可见性与有序性

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring - 基本用法参考

Spring 官方文档 Spring容器启动流程(源码解读) BeanFactoryPostProcessor vs BeanPostProcessor vs BeanDefinitionRegistryPostProcessor: From java doc: BeanFactoryPostProcessor may interact with and modify bean defin…

网工内推 | 申通快递急招网安、测试工程师,包食宿,30k*13薪

01 申通快递 招聘岗位:信息安全工程师 职责描述: 1、 负责集团数据安全风险的识别、协同、跟踪、改进优化及事后评估; 2、 负责集团数据安全专项风险的治理及系统上线前的数据安全评审; 3、 负责集团信息安全、合规等方面制度的编…

限时回归!!!3D版《空洞骑士》!!!

空洞骑士是一款基于横板平台跳跃的传统风格2D动作冒险游戏。庞大的游戏世界交错相通,玩家控制小虫子去探索幽深黑暗的洞穴,成为了一代人茶余饭后的惦念,深受广大玩家们的喜爱。 这类平台跳跃游戏一般是游戏开发初学者以及独立游戏开发者们比…

React一学就会(7): 细说redux及其应用

不知不觉一个星期结束了,很快就要过年了,中间休息了两天,小孩生病,我也有点感冒了,还好,我的这个 React 基础教学课程也基本结束了。大家有不明白的可以留言问我,我一定竭尽所能的帮助你。后面几…

D3703F——应用于音响系统的静音检测电路 当音响系统在放音或快进 / 退时进行静音检测,输出控制信号。

D3703F 是 一 块 汽 车 音 响 静 音 检 测 电 路 。 用 于 音 响 系 统 检 测 在 放 音 或 快 进 / 退 时 进 行 静 音 检 测 。 D3703F 的 的 电 压 范 围 : 3.2V ~ 16V , 信 号 检 测 和 静 音 时 间 可 通 过 外 围 电 阻 、 电 容 来 …

中小型企业知识库建设的秘诀来啦,赶紧收藏起来

知识库是企业的智慧宝库,其中的信息和知识的整合,可以极大地提高工作效率和团队协作能力。尤其对中小企业来说,知识库的建设更是关系企业未来发展的重要因素。那么,怎样有效地构建高效的知识库系统呢?下面这些秘诀值得…

Arthas的使用

1. 简介 官网 线上debug神器,就不过多介绍 2. 环境搭建 win11环境 ,jdk11 2.1 安装 下载地址 2.2 启动 cmd java -jar arthas-boot.jar启动之后会自动检测启动的java服务 1~4 ,springboot是启动类名,所以我选择了3 3. 常用操作 3.…

Hadoop3.x基础(1)

来源:B站尚硅谷 这里写目录标题 大数据概论大数据概念大数据特点(4V)大数据应用场景 Hadoop概述Hadoop是什么Hadoop发展历史(了解)Hadoop三大发行版本(了解)Hadoop优势(4高)Hadoop组成&#xf…

非阿里云注册域名如何在云解析DNS设置解析?

概述 非阿里云注册域名使用云解析DNS,按照如下步骤: 添加域名。 添加解析记录。 修改DNS服务器。 DNS服务器变更全球同步,等待48小时。 添加解析记录 登录云解析DNS产品控制台。 在 域名解析 页面中,单击 添加域名 。 在 …

SkyWalking+es部署与使用

第一步下载skywalking :http://skywalking.apache.org/downloads/ 第二步下载es:https://www.elastic.co/cn/downloads/elasticsearch 注:skywalking 和es要版本对应,可从下面连接查看版本对应关系,8.5.0为skywalking 版本号 Index of /di…

实惨!多本EI接连被各大数据库剔除!2024年EI期刊目录首次更新-附下载

EI目录更新 本月爱思唯尔(Elsevier)官网更新了EI Compendex收录期刊目录,这是2024年第一次更新。 Elsevier发布2024年第一版EI期刊目录 更新时间:2024年1月1日 不同于SCI/SSCI目录每月更新一次的频率,EI目录更新没有…

VBA技术资料MF112:列出目录中的所有文件和文件夹

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程一共九套,分为初级、中级、高级三大部分。是对VBA的系统讲解,从简单的入门,到…

构建高效外卖系统:利用Spring Boot框架实现

在当今快节奏的生活中,外卖系统已经成为人们生活中不可或缺的一部分。为了构建一个高效、可靠的外卖系统,我们可以利用Spring Boot框架来实现。本文将介绍如何利用Spring Boot框架构建一个简单但功能完善的外卖系统,并提供相关的技术代码示例…

解析线上HBase集群CPU飙高的原因与解决方案

在日常的运维工作中,CPU负载高是一种常见的故障状况,它可能对系统的正常运行和性能产生不利影响。为了准确地定位具体的异常原因,掌握一些专业的工具和方法是至关重要的。本文将通过一个实际的案例,详细介绍如何排查在线上HBASE集…

【C++】C++入门基础讲解(二)

💗个人主页💗 ⭐个人专栏——C学习⭐ 💫点击关注🤩一起学习C语言💯💫 导读 接着上一篇的内容继续学习,今天我们需要重点学习引用。 1. 引用 在C中,引用是一种特殊的变量&#xff…

动态gif图如何在线做?这一招分分钟生成

Gif动图是怎么制作呢?Gif动画已经是日常聊天娱乐必备的了,那么这种有趣的gif表情要怎么操作呢?很简单,使用gif动图生成(https://www.gif.cn/)工具无需下载软件,小白也能轻松操作。可上传MP4格式…

Vue2 VS Vue3 生命周期

一、生命周期的概念 Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子(也会叫…

每日一道面试题:Java中序列化与反序列化

写在开头 哈喽大家好,在高铁上码字的感觉是真不爽啊,小桌板又拥挤,旁边的小朋友也比较的吵闹,影响思绪,但这丝毫不影响咱学习的劲头!哈哈哈,在这喧哗的车厢中,思考着这样的一个问题…

对Spring当中AOP的理解

AOP(面向切面编程)全称Aspect Oriented Programminge AOP就是把系统中重复的代码抽取出来,单独开发,在系统需要时,使用动态代理技术,在不修改源码的基础上,将单独开发的功能通知织入(应用)到系统中的过程,完…

shopee,lazada卖家自养号测评补单的方法和技巧

现在很多卖家都是自己管理几百个账号,交给服务商不是特别靠谱 一:送测不及时,产品时常送不出去 二:账号质量不稳定,账号一天下了多少你也不清楚,如果下了很多单万一封号被关联了怎么办 三:as…