Java 面试宝典:什么是可见性?volatile 是如何保证可见性的?

大家好,我是大明哥,一个专注「死磕 Java」系列创作的硬核程序员。
本文已收录到我的技术网站:https://skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经


什么是可见性?

可见性是指一个线程对共享变量所作的修改能够被其他线程及时地看到。

在单核时代,其实是不存在可见性问题的,因为所有的线程都是在一个CPU中工作的,一个线程的写操作对于其他的线程一定是可见的。

但是,在多核时代,每个 CPU 都有自己的缓存。一个线程对共享变量的修改可能只是在它所在 CPU 的本地缓存中进行,而不是在主内存中进行。这就可能导致其他线程看不到这个修改,从而引发可见性问题。

解决可见性的方案有两种:

  1. 使用 volatile 修饰共享变量:一个变量被声明为 volatile 后,对这个变量的读写操作都是在主内存中进行的,从而保证了不同线程之间对该变量修改的可见性。
  2. 使用同步机制,比如锁或者 synchronized。当一个线程成功获取锁进入一个同步块时,它会看到由其他线程在相同同步块内对共享变量的修改。

volatile 是如何保证可见性的?

这部分内容在 volatile 的实现原理中有,但是为了更好地阅读,大明哥直接复制过来了。

对于 volatile 变量,会在写入 volatile 变量的指令前添加 lock 前缀(汇编层面),当某个线程写入 volatile 变量时,其值会被强制刷入主内存,而其他处理器的缓存由于遵守了缓存一致性协议(MESI 协议),其他处理器的工作内存会被标志为无效。当其他处理器来访问这个变量时,由于它们的本地缓存是无效的,它们就不得不从主内存中重新加载这个变量的最新值。这样就保证了线程的可见性。

lock 前缀是用于实现原子操作的一种机制。当它用于一个指令前,它会锁定一个特定的内存地址,确保该指令执行期间,该内存地址不会被其他处理器访问。

MESI 协议

MESI协议,即缓存一致性协议,它是一种用于维护多处理器系统中缓存一致性的协议。从上面我们知道,每个处理器都有自己的工作内存,这可能导致同一内存位置的多个副本同时存在于不同的缓存中。为了保证这些副本的一致性,引入 MESI 协议来保证一致性。

其核心思想:当 CPU 写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

MESI 代表四种缓存行状态:Modified(修改)、Exclusive(独占)、Shared(共享)和Invalid(无效)。

  1. Modified(修改):数据有效,数据已被修改,且只存在于当前缓冲中。这个状态下的缓存行数据于主内存数据不一致,在数据被写回主内存之前,任何对这个缓存行的读取或写入操作都只会发生在这个缓存中。
  2. Exclusive(独占):数据有效,且只存在于当前缓存中。这个状态下的缓存行数据与主内存中的数据是一致的。如果 CPU 需要写入这个缓存行,它可以直接改变状态到Modified,而无需与其他处理器或主内存通信。
  3. Shared(共享):数据有效,且可能存在于多个 CPU 的缓存中,并且数据与主内存中的数据是一致的。这个状态下的缓存行任何 CPU 都可以读到,但如果某个 CPU 需要写入,它必须首先通知其他拥有该缓存行副本的 CPU,使它们的副本无效。
  4. Invalid(无效):数据无效。如果有 CPU 需要读取这个缓存行数据,它必须从拥有有效副本的其他缓存或主内存中读取数据。

其工作流程如下:

读数据

  • 如果数据在本地缓存中并且状态是 Modified、Exclusive 或 Shared,处理器直接从缓存中读取,因为这三种状态的数据是有效的。
  • 如果数据不在本地缓存中,或者缓存行状态是 Invalid,处理器向其他缓存发送读取请求:
    • 如果其他缓存中没有该数据,或者都是 Invalid 状态,处理器从主内存读取数据,并将本地缓存行状态设置为 Exclusive。
    • 如果其他缓存中有该数据并且至少一个是 Shared 或 Modified 状态,处理器从拥有该数据的缓存复制数据,并将所有拥有该数据的缓存行状态设置为 Shared。

写数据

  • 如果数据在本地缓存且状态是 Modified,处理器直接写入本地缓存。
  • 如果数据在本地缓存且状态是 Exclusive,处理器将缓存行状态改为 Modified,并执行写入操作。
  • 如果数据在本地缓存且状态是 Shared 或者不在本地缓存中,处理器向其他缓存发送失效通知:
    • 其他缓存如果有该数据,则将其缓存行状态设置为 Invalid。
    • 本地缓存将数据写入,并将缓存行状态设置为 Modified。

内存屏障

volatile 通过在在每个读操作前都加上**Load屏障,强制从主内存读取最新的数据,在每个写操作后加上Store屏障,强制将数据刷新到主内存。**这样每次写都能将最新数据刷入到主内存,读都能从主内存读取最新数据,以此达到可见性。

下面以 i++ 为例来阐述下:

如上图所示,流程如下:

  • 线程 A 读取 i 时,遇到 Load 屏障,需要强制从主内存中读取得到 i = 0,加载到工作内存中。
  • 线程 A 执行 i++ 操作得到 i = 1,执行 assign指令进行赋值,遇到 Store 屏障,需要将 i = 1 强制刷新回主内存,此时主内存数据 i = 1
  • 然后线程 B 读取 i,也遇到Load 屏障,强制从主内存读取 i 的最新值, i = 1,执行 i++ 操作,得到 i = 2,同样在执行 assign 赋值后,遇到Store屏障立即将数据刷新回主内存,此时主内存数据 i = 2

这里可能有小伙们会认为,线程 A 和线程 B 同时执行,都从主内存读取 i = 0,然后执行 i++,最后主内存数据 i = 1,会不会存在这种情况?会,但是我们通过同步机制让他们不会,为什么?因为这个操作不是原子操作,在并发情况下会产生线程安全问题,我们是需要采用同步或者锁机制来保护的。

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

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

相关文章

C语言例4-5:getchar()函数的调用格式和使用的例子

代码如下&#xff1a; //getchar()函数的调用格式和使用的例子 #include<stdio.h> //编译预处理命令&#xff0c;即文件包含命令 int main(void) {char ch1, ch2;int a;ch1 getchar(); //从键盘输入一个字符并赋予字符型变量ch1ch2 getchar(); //从键盘输入一个…

基于Java实验室预约管理系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

C语言例4-20:从键盘输入年和月,输出该月的天数。

代码如下&#xff1a; //从键盘输入年和月&#xff0c;输出该月的天数。 #include<stdio.h> int main(void) {int year, month, days;printf("输入年和月&#xff1f;\n");scanf("%d,%d",&year,&month); //从键盘输入年和月switch(month)…

本地大模型(免安装绿色版)

上一篇文章我们讨论了如何在本地运行大模型 。 安装本地大模型 大部分软件都需要安装&#xff0c;今天是免安装绿色版。 很多人下载不了原版&#xff0c;所以我放百度网盘了。 下载 下载&#xff1a;llava-1.5大模型(7b参数,流量警告:4G) 运行&#xff1a;.\llava-v1.5-7b-…

codeforces round.906 - E - Mirror Grid (数学,坐标变换)

You are given a square grid with n rows and n columns. Each cell contains either 0 0 0 or 1 1 1. In an operation, you can select a cell of the grid and flip it (from 0 → 1 0→1 0→1 or 1 → 0 1→0 1→0). Find the minimum number of operations you need…

TSINGSEE青犀智慧充电桩消防安全烟雾火焰AI算法识别预警方案

一、方案背景 随着AI人工智能、大数据、云计算等技术快速发展与落地&#xff0c;视频智能分析技术在智慧充电桩场景中的应用也越来越广泛。这种技术能够为充电桩站点提供全方位的监控和管理&#xff0c;提高运营效率&#xff0c;保障充电站设备的安全和稳定运行。 通过TSINGS…

第四百二十六回

文章目录 1. 概念介绍2. 实现方法2.1 原生方式2.1 插件方式 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何修改程序的桌面图标"相关的内容&#xff0c;本章回中将介绍如何处理ListView中的事件冲突.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介…

Web APIs知识点讲解(阶段三)

DOM- 节点操作 一.节点操作 1.DOM节点 目标&#xff1a;能说出DOM节点的类型 DOM节点 DOM树里每一个内容都称之为节点 节点类型 元素节点 所有的标签 比如 body、 div html 是根节点 属性节点 所有的属性 比如 href 文本节点 所有的文本 document树&#xff1a; 总结&…

你敢信?苹果iPhone 16将搭载百度AI文心一言

【行业焦点】苹果公司&#xff0c;这位全球消费电子舞台的常青树&#xff0c;近期传出一则震撼业界的消息&#xff1a;其将在新一代iPhone 16系列中国版产品中&#xff0c;深度融合百度的尖端生成式AI技术。这一重大决策不仅揭示了苹果对中国市场的深度耕耘与本地化创新战略&am…

Java Web-Maven

Maven是apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具 Maven的作用 1.依赖管理:方便快捷的管理项目依赖资源(jar包)&#xff0c;避免版本冲突问题 我们有的项目需要大量的jar包&#xff0c;采用手动导包的方式非常繁琐&#xff0c;并且版本升级也…

elasticsearch _cat/indices docs.count is different than <index>/_count

今天遇到一个问题&#xff0c;kibana中看到文档数与下面语句查询到的不同 GET /_cat/count/jiankunking_xxxxx_product_expand_test?v GET /jiankunking_xxxxx_product_expand_test/_search?track_total_hitstrue语句查询结果 epoch timestamp count 1711433785 06:16…

外贸企业如何访问外网?

在当今全球化的浪潮下&#xff0c;企业对于稳定、高效的网络连接需求日益迫切。无论是在境内还是境外&#xff0c;企业都需要依托可靠的网络基础设施&#xff0c;以确保业务的顺畅进行。在这样的背景下&#xff0c;我们提供的专业网络运营服务成为了众多企业的首选。 1. 网络建…

neo4j使用详解(三、cypher插入语法——最全参考)

1.插入语法 create, merge, set, delete, remove, create unique, load csv(import) 除插入语法外另外的语法详情请博主其他文章&#xff1a; 查询语法 其他语法 1.1.create语法 创建单个节点: create(n) # (ID:n) 创建带标签的节点: create (n:Person) # (ID:n, label:Perso…

垃圾回收:垃圾回收器

目录 垃圾回收器 评估GC的性能指标 7种典型的垃圾回收器 Serial回收器&#xff1a;串行回收 ParNew回收器&#xff1a;并行回收 Parallel回收器&#xff1a;吞吐量优先 CMS回收器&#xff1a;低延迟 G1回收器&#xff1a;区域化分代式 G1回收过程1-年轻代GC G1回收过程…

人、机中的幻觉和直觉

对于人类而言&#xff0c;幻觉和直觉是两种不同的心理现象。幻觉是一种错误的感知或体验&#xff0c;而直觉是一种在没有明显依据的情况下产生的直观认知。这两种概念在心理学和认知科学中具有不同的意义和研究对象。 人类幻觉是指个体在感知或体验上出现的错误&#xff0c;即看…

攻防世界-baby_web

题目信息 相关知识 使用bp进行抓包 解题过程 题目界面如下所示: 试图找index界面&#xff1a; 发现又跳转到http://61.147.171.105:51201/1.php页面&#xff0c;因此说明61.147.171.105:51201/index.php是存在的&#xff08;因为笔者试了&#xff0c;不存在的页面会直接报…

SHAP和LIME:Python机器学习模型解释

SHAP和LIME&#xff1a;Python机器学习模型解释 为了更好地展示&#xff0c;示例代码参见和鲸社区&#xff0c;一键运行 简介 机器学习模型在各个领域得到广泛应用&#xff0c;但其黑盒性质往往使人难以理解其决策过程&#xff0c;降低了模型的可信度和可靠性。为了解决这一…

ROM-IP

1.原理 通过添加数据文件&#xff0c;使ROM看起来不是易失性存储器&#xff0c; 产生256个数据&#xff0c;每个数据的位宽是8 如果前面为x&#xff0c;后面就是x256-1 2.单端口ROM配置 FPGA内部没有非易失性存储器。调用的ROM和RAM都是用RAM来生成的 3.双端口ROM配置 使用第一…

React系列之虚拟DOM、FIBER和DIFF算法

文章目录 虚拟 DOM 和 DIFF 算法虚拟DOM虚拟DOM对象虚拟DOM的优势预防XSS DIFF算法旧的DIFF算法Fiber树渲染过程算法过程key 的作用 虚拟 DOM 和 DIFF 算法 虚拟DOM React使用虚拟DOM来更新真正的DOM。 DOM表示“文档对象模型”&#xff0c;浏览器遵循HTML指令来构造文档对象…

Temu api接口 获取商品详情 数据采集

iDataRiver平台 https://www.idatariver.com/zh-cn/ 提供开箱即用的Temu电商数据采集API&#xff0c;供用户按需调用。 接口使用详情请参考Temu接口文档 接口列表 1. 获取商品详情 参数类型是否必填默认值示例值描述apikeystring是idr_***从控制台里复制apikeycountrystrin…