Java 内存可见性与volatile

2023-11-02

在多核系统中,处理器一般有一层或者多层的缓存,这些的缓存通过加速数据访问(因为数据距离处理器更近)和降低共享内存在总线上的通讯(因为本地缓存能够满足许多内存操作)来提高CPU性能。如图:处理器的多层缓存模型



 JVM需要实现跨平台的支持,它需要有一套自己的同步协议来屏蔽掉各种底层硬件和操作系统的不同,因此就引入了Java内存模型JMM。

JMM ( Java Memory Model )主要是为了规定了线程和内存之间的一些关系。系统存在一个主内存 (Main Memory) , Java 中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存 (Working Memory) ,工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。如图: Java 内存模型
在java内存模型中,共享变量会在多线程中存在可见性的问题。如下面代码中的例子:
private static boolean ready
…..
Thread1,2,3…
while (!ready) {
  // do something unready…
}
// do something ready
……
Thread x….
ready = true;
 当Thread x中设置ready = true时,会将该值写入工作内存,并同步到主内存,但其他线程有可能还是读取到自己工作内存中缓存的老数据,从而导致其他线程可能看不到ready=true而不跳出循环,以上就是一个典型的java内存可见性问题。
 
当然java内存模型也定义了一系列解决可见性(工作内存和主内存交互协议)方法,包括volatile,synchronized,锁等方式,这里主要用volatile来说明可见性问题。
先看volatile的定义:
 java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

调整前面示例代码中共享变量为volatile并写出完整的测试代码:

public class VisibilityDemo {

    private static volatile boolean ready;

    static class Reader extends Thread {
        @Override
        public void run() {
            long tryTimes = 0L;
            while (!ready) {
                ++tryTimes;
            }
            System.out.println("ready! try times : " + tryTimes);
        }
    }

    static class Writer extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                ready = true;
            }
        }
    }

    public static void main(String[] args) throws Exception{
        new Reader().start();

        Thread.sleep(100L);

        new Writer().start();
    }
}

 加了volatile后Reader线程能成功退出并打印出tryTimes。

用javap –c –l –s –verbose VisibilityDemo 查看增加volatile前后的字节码没有区别,直接看JIT运行时汇编码:

环境:

~$ file /sbin/init
/sbin/init: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7d9cc5d4d6cb68aede9400492a7c5942c55c7598, stripped
~$ java -version
java version "1.7.0_55"
Java(TM) SE Runtime Environment (build 1.7.0_55-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode)

 打印JIT汇编码:

Java -client -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly VisibilityDemo > ./ VisibilityDemo.assemblycode

 

0x00007f02c8c3b2d6: mov    %sil,0x70(%r10)
0x00007f02c8c3b2da: lock addl $0x0,(%rsp)     ;*putstatic ready

 

 有volatile变量修饰的共享变量进行写操作的时候会多第二行汇编代码,通过查IA-32架构软件开发者手册可知,lock前缀的指令在多核处理器下会引发了两件事情:
将当前处理器缓存行的数据会写回到系统内存。
这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。
 
处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
 
Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声言处理器的 LOCK# 信号。在多处理器环境中,LOCK# 信号确保在声言该信号期间,处理器可以独占使用任何共享内存。(因为它会锁住总线,导致其他CPU不能访问总线,不能访问总线就意味着不能访问系统内存),但是在最近的处理器里,LOCK#信号一般不锁总线,而是锁缓存,毕竟锁总线开销比较大。但在P6和最近的处理器中,如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反地,它会锁定这块内存区域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据。

 

 

参考文章:http://ifeve.com/volatile/

分析生成的汇编代码 http://blog.csdn.net/hengyunabc/article/details/26898657

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 内存可见性与volatile 的相关文章

  • 获取 UndeclaredThrowableException 而不是我自己的异常

    我有以下代码 public Object handlePermission ProceedingJoinPoint joinPoint RequirePermission permission throws AccessException
  • 如何在itext中设置自定义颜色?

    感谢您花时间回答我的问题 我正在使用 Java 中的 iText 生成 PDF 文档 我需要将表的列标题设置为与值列中的颜色不同的颜色 我有来自 Photoshop 的颜色十六进制值 我正在使用带有块和段落的 PdfPTable 除了 Ba
  • Spring MVC 应用程序可以是多线程的,即使它的 servlet 不是吗?

    当您谈论 Spring 应用程序是多线程时 您是否一定是指该应用程序中定义的 servlet 是否是多线程的 或者即使应用程序中的 servlet 不是多线程 Spring 应用程序也可以配置为多线程吗 不再支持单线程 servlet 它们
  • 在 Postgres 中为特定查询设置 work_mem

    我正在使用一个委托给 JDBC 驱动程序的库PostgreSQL 而且有些查询非常复杂 需要更多内存 我不想设置work mem对于所有查询来说都是大的 只是这个子集 问题是执行以下代码会导致错误 pseudo code for what
  • 如何使用 hibernate 过滤器过滤 hibernate 中的实体

    我需要过滤对象列表中的实体 例如 public class Student private int id private List
  • 使用Keycloak保护Tomcat应用程序时出现HTTP 403禁止错误

    我为这个错误苦苦挣扎了一整天 我一遍又一遍地检查我在tomcat中Keycloak和APP的配置 没有发现错误 下图为测试场景 APP配置 1 Keycloak json是从Keycloak控制台复制的 2 context xml 也正确
  • Android - 使用 Intent 打开 PDF 文档关闭后不保存

    我面临的问题是 当尝试保存对使用此 URI 打开的 PDF 文档的更改时内容 xx xxx xxx fileprovider external Download Sync FileName pdf 我所做的任何更改在关闭文档后都不会保存 但
  • 在 Java 中停止线程? [复制]

    这个问题在这里已经有答案了 我正在编写一段代码 该代码连接到服务器 使用该连接生成一堆线程并执行一堆 东西 在某些情况下 连接会失败 我需要停止一切并从头开始使用新对象 我想在对象之后进行清理 但在线程上调用 thread stop 但此方
  • 如何在Intellij IDEA中复制maven依赖项到输出工件WEB-INF/lib?

    我知道在 eclipse maven 中可以将依赖项复制到 WEB INF lib 目录setting Deployment Assembly and Build Path 但是我怎样才能用 Intellij IDEA 实现这一点呢 好的
  • 如何跨工作区保存 E​​clipse 启动配置文件?

    当我复制 Eclipse 项目目录时 它包含 classpath 和 project 文件 这样当我将同一目录带到另一个 Eclipse 实例时 我不必设置我的构建路径等 假设所有资源都包含在在项目中 而不是外部 但是 此过程不会导致启动配
  • 如何知道 Solr Optimize 何时完成?

    我正在使用 Solr php client 通过 php 与 Solr 进行通信 这段代码触发solr优化命令 solr gt optimize 请问有没有什么方法可以确定优化完成了 这都是因为我的网站上有一个管理页面 我每天必须手动优化
  • 查找前 N 个五边形数

    我必须找到第一个N pentagonal numbers 1 从 1 100 并每行显示 10 个 我必须使用getPentagonalNumber int n 方法也是如此 显然这就是它存在的原因 到目前为止 这是我的代码 package
  • java代码的等效vb代码

    谁能告诉我这段Java代码到底做了什么 SecureRandom random SecureRandom getInstance SHA1PRNG byte bytes new byte 20 synchronized random ran
  • Maven 依赖冲突:org.w3c.dom.ElementTraversal

    我有一个 Java 代码库 它使用 Maven 进行依赖项解析并在 CI 上运行测试 经过最近的一批开发 大到足以很难识别重大更改 我的一些测试现在在通过 Maven 运行时失败了NoClassDefFoundError for org w
  • 如何删除 Spring 的 RestTemplate 添加的某些 HTTP 标头?

    我在远程服务方面遇到问题 我无法控制对使用 Spring 的 RestTemplate 发送的请求进行 HTTP 400 响应 使用发送的请求curl但被接受了 所以我将它们与通过 RestTemplate 发送的内容进行了比较 特别是 S
  • 我们可以将请求分派到 servlet 内的 HTML

    这可能吗 RequestDispatcher rd request getRequestDispatcher index html rd forward request response 是的 您可以将请求分派到 HTML 页面
  • 使用用户名和密码登录 LinkedIn 失败

    LinkedIn使用oauth登录其api 服务器中无法登录api 我尝试使用http请求登录linkedin并获取oauth verifier 但我得到了这样的回应 很抱歉 出现了问题 你的申请 请确保您 启用cookie并重试 或点击此
  • Selenium 查看鼠标/指针

    有什么方法可以在运行测试时真正看到硒鼠标吗 要么是 Windows 光标图像 要么是某种点或十字线或任何东西 我正在尝试使用拖放功能selenium and java in an HTML5Web 应用程序 并且能够看到光标以了解它实际在做
  • 当列表中不存在 X 时,从列表中查找大于 X 的值

    我试图从列表中查找大于特定值 在我的情况下已知 的值 Example Given list 1 2 5 10 15 list is sorted 查找大于的值X 7在这种情况下 期望的结果 返回一个包含值的列表 10 15 我尝试使用jav
  • 应用服务器如何注入私有字段?

    我看到这个问题 注入私有 包或公共字段或提供 setter https stackoverflow com questions 2021716 inject into private package or public field or p

随机推荐

  • 论文阅读《LGPMA:Complicated Table Structure Recognition with Local and Global Pyramid Mask Alignment》

    摘要 表格识别是一项很有挑战的任务 以前的方法从不同粒度的元素 行 列 文本区域 开始处理问题 这从某种程度上有损启发式规则 忽略了空细胞分裂等问题 基于表结构特征 我们发现获取文本区域的对齐bounding box可以有效地保持不同单元格
  • Intellij IDEA 导入 eclipse web 项目详细操作

    Eclipse当中的web项目都会有这两个文件 但是idea当中应该是没有的 所以导入会出现兼容问题 但是本篇文章会教大家如何导入 并且导入过后还能使用tomcat运行 文章尽可能以图片的形式进行演示 我的idea使用的版本是2022 3
  • UE4_读写内容到文本文件

    1 新建一个c 文件 类型为BlueprintFunctionLibrary RWTextFile h Fill out your copyright notice in the Description page of Project Se
  • 谷歌云计算技术基础架构,谷歌卷积神经网络

    谷歌开源了TensorFlow 世界就要马上被改变了吗 Google开源了其第二代深度学习技术TensorFlow 被使用在Google搜索 图像识别以及邮箱的深度学习框架 这在相关媒体圈 工程师圈 人工智能公司 人工智能研究团队里有了一些
  • ajax调用java程序,从微信小程序到鸿蒙JS开发-JS调用Java

    除轻量级智能穿戴设备 现鸿蒙支持的手机 汽车 TV 手表 平板等属于富鸿蒙 在JS语言的项目中也有Java模块 并提供了JS跨语言调用Java方法的技术 现需要实现查看商品评论时 统计出长评 中评和短评的比例 这里将评论数据请求来后调用Ja
  • linux下内核态锁与用户态锁详细介绍

    1 内核态下锁 1 1 spinlock t spinlock t成为自旋锁 它用在临界区代码非常少的情况下 自旋锁不会引起调用者睡眠 如果自旋锁已经被别的执行单元保持 调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁 如果释放了该
  • 播放声音

    声音类型 load 上料 switchs 换装 changes 换程 private enum Sound load switchs changes SoundPlayer sp new SoundPlayer
  • 在.NET中杀死Word,Excel等进程

    下面的方法可以直接调用 private void KillProcess string processName System Diagnostics Process myproc new System Diagnostics Process
  • 【Learning RAW-to-sRGB Mappings with Inaccurately Aligned Supervision通过不准确对齐的监督学习 RAW 到 sRGB 的映射】

    摘要 学习 RAW 到 sRGB 映射近年来引起了越来越多的关注 其中训练输入的原始图像以模仿另一台相机捕获的目标 sRGB 图像 然而 严重的颜色不一致使得生成输入原始和目标 sRGB 图像的良好对齐训练对非常具有挑战性 虽然使用不准确对
  • 解决java解析XML文件时的“伪属性名称”问题

    解决java解析XML文件时的 伪属性名称 问题 一定是xml文件写错了格式
  • apk部分手机安装失败_安卓手机安装软件失败的4种常见原因和解决办法

    安卓手机安装软件是普通人都会做的事情 不过这也是有可能会出现问题的 比如安卓手机安装软件失败了 这要怎么解决 安卓手机安装软件失败是一些新手用户可能会遇到的问题 虽然有很多方面的原因 不过大体上分为软件和硬件2种情况 下面就整理一些解决方法
  • Unity初学者对物体移动的总结

    Unity小白笔记文章 请大家多多指教 关于Unity3D控制物体移动的常用方法 首先控制物体移动即控制物体的空间坐标变化 在这里首先我们要知道Input输入事件 一般大家先想到的都是按一个按键去控制物体移动 Unity里也给我们提供了这种
  • Web前端之如何描述自己做过的项目

    在面试时 经过寒暄后 一般面试官会让介绍项目经验 常见的问法是 说下你最近的 或最拿得 出手的 一个项目 根据我们的面试经验 发现有不少候选人对此没准备 说起来磕磕巴巴 甚至有人说出项目经验从时间 段或技术等方面和简历上的不匹配 这样就会造
  • Unity 弓箭射靶游戏实践

    一 实现思路 根据之前的飞碟工厂进行改变 在射出弓箭手上没有弓箭之后重新生成新的弓箭 并将射出的弓箭在一定时间后进行回收 在右下角通过小窗口展示靶子的情况 射中不同的环数给予不同得分 二 主要涉及技术 物理引擎的使用 游戏对象的生产与回收
  • 关于autorelease pool一个较好的理解

    如果你能够真正的理解autorelease 那么你才是理解了Objective c的内存管理 Autorelease实际上只是把对release的调用延迟了 对于每一个Autorelease 系统只是把该Object放入了当前的Autore
  • 第二十三章 模块代码编写基础

    模块的创建 python中的所有 py文件都能做为模块 模块文件名 模块的命名应该遵循一般变量名的命名规则 模块的使用 import语句 import语句直接列出一个或多个需要加载的模块的名称 以逗号分隔 因为它用一个名称引用整个模块 im
  • Docker配置本地镜像与容器的存储位置

    使用find命令找到大于指定大小的文件 find type f size 10G 排除某个目录 find path media xww type f size 10G 修改Docker本地镜像与容器的存储位置的方法 方法一 软链接 默认情况
  • Qt程序crash信息的捕捉与跟踪(qt-mingw)

    在用qt编写程序时经常会遇到崩溃问题 如果抓取不到crash堆栈信息就会对崩溃问题束手无策 只能对其进行复现 推断 目录 一般解决crash问题时有如下步骤 如何执行以上3步骤 下面我详细介绍如何操作 步骤1 步骤2 步骤3 网友评论 一般
  • js取消默认事件和事件绑定

    1 默认事件 浏览器本事具备的一些功能 如鼠标右键菜单 a标签跳转页面 如果要阻止这些默认行为 可以用return false w3c中定义了ev preventDefault 这个不兼容IE11以下
  • Java 内存可见性与volatile

    在多核系统中 处理器一般有一层或者多层的缓存 这些的缓存通过加速数据访问 因为数据距离处理器更近 和降低共享内存在总线上的通讯 因为本地缓存能够满足许多内存操作 来提高CPU性能 如图 处理器的多层缓存模型 JVM需要实现跨平台的支持 它需