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

news2024/11/20 6:12:35

大家好,我是大明哥,一个专注「死磕 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,会不会存在这种情况?会,但是我们通过同步机制让他们不会,为什么?因为这个操作不是原子操作,在并发情况下会产生线程安全问题,我们是需要采用同步或者锁机制来保护的。

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

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

相关文章

数据结构(五)单链表专题

在开始之前,我先来给大家讲一下顺序表与链表的区别: 它们在堆上存储的差异: 我们可以很容易的知道,循序表是连续的有序的,但链表是杂乱的,它们通过地址彼此联系起来。 1. 链表的概念及结构 概念&#xff1…

阳光倒灌高准直汽车抬头显示器HUD太阳光模拟器

阳光倒灌高准直汽车抬头显示器HUD太阳光模拟器是一种高级别的模拟设备,用于模拟太阳光的光谱、强度及照射角度,应用于太阳能电池板、光伏系统等领域的研究和测试。其参数包括光谱范围、光强度、光源、照射角度、均匀性和稳定性,可根据需求调整…

java Web会议信息管理系统 用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 jsp 会议信息管理系统是一套完善的web设计系统,对理解JSP java SERLVET mvc编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发,数据库为Mysql5.0&am…

数据结构——队列(C语言版)

前言: 在学习完数据结构顺序表和链表之后,其实我们就可以做很多事情了,后面的栈和队列,其实就是对前面的顺序表和链表的灵活运用,今天我们就来学习一下队列的原理和应用。 准备工作:本人习惯将文件放在test…

yolov5+pyside6+登录+用户管理目标检测可视化源码

一、软件简介 这是基于yolov5目标检测实现的源码,提供了用户登录功能界面; 用户需要输入正确的用户名和密码才可以登录。如果是超级管理员,可以修改普通用户的信息,并且在检测界面的右上角显示【管理用户】按钮。 支持图片、视频、…

RN封装的底部向上弹出的弹出层组件

组件代码 import React from react; import { View, StyleSheet, Modal, TouchableOpacity, Text, TouchableWithoutFeedback } from react-native;const BottomPopup ({ visible, onClose, children, leftButtonTitle, rightButtonTitle, onLeftButtonPress, onRightButtonP…

【前端】-【性能优化常识】

目录 前端性能优化指标首屏速度、白屏时间性能优化收效很大的操作:减少首屏资源体积收效不大或者特殊情况的优化操作 操作速度、渲染速度造成操作卡顿和渲染慢的场景性能优化 数据缓存 补充知识异步加载加载方式一:prefetch加载加载方式二:sc…

如何查询电脑是否被锁定了IP地址?锁定IP会出现什么问题?

前言 电脑刚到手的时候,基本上是通过路由器DHCP进行IP分配的。路由器DHCP分配IP给电脑的好处是网络不会出现IP冲突,网络能正常使用。 有些电脑可能在DHCP自动获取IP时出现错误,所以小伙伴就会通过手动设置IP让电脑可以正常上网。 这样的操…

[CISCN2019 华东北赛区]Web2

[CISCN2019 华东北赛区]Web2 随便注册一个登录,发现 还有反馈页面,一看就知道大概率是xss,应该是为了得到管理员cookie扫描了一下,果然有admin.php后台登录 buu可以连接访问外网了,所以内部的xss平台关闭了&#xff0…

【P1328】[NOIP2014 提高组] 生活大爆炸版石头剪刀布

[NOIP2014 提高组] 生活大爆炸版石头剪刀布 题目背景 NOIP2014 提高组 D1T1 题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种…

【笔记】OpenHarmony设备开发:搭建开发环境(Ubuntu 20.04,VirtualBox 7.0.14)

参考:搭建开发环境(HarmonyOS Device) Note:Windows系统虚拟机中Ubuntu系统安装完成后,根据指导完成Ubuntu20.04基础环境配置(HarmonyOS Connect 开发工具系列课) 系统要求 Windows系统要求&…

Warning logs 2024-03-23

给旧的笔记本安装ubuntu系统,并实现ssh远程连接 1、下载ubuntu系统 ubuntu下载链接 选择带桌面版本 2、准备U盘 3、使用UltraISO制作启动盘 使用UltraISO,打开刚才下载的ubuntu**.iso文件 4、进入BIOS,选择U盘启动 5、Warning 1 invali…

[Android]创建Google Play内购aab白包

开发时需要调试Google内购,需要先往Google商店传一个白包上去。确定包名,然后进行内购产品创建。 1.创建一个空项目,填写正式名称和正式包名。 如果你只是为一个测试开发账号打白包,然后进行内购测试,这时包名随便写…

深度学习中常用计算距离的几种算法对比与python实现

前言 距离度量在许多机器学习算法中扮演着至关重要的角色,无论是监督学习还是无监督学习。选择适当的距离度量可以显著影响模型的性能。 在高维数据集中,欧几里得距离可能会受到所谓的“维度诅咒”的影响,因为随着维度的增加,数…

如何打包springboot项目并部署服务器

创建一个springboot项目&#xff0c;先写一个接口&#xff0c;我这里是dabaimao/jiekou,启动访问 在pom中加上maven插件 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin<…

Qt 图形视图 /图形视图框架坐标系统的设计理念和使用方法

文章目录 概述Qt 坐标系统图形视图的渲染过程Item图形项坐标系Scene场景坐标系View视图坐标系map坐标映射场景坐标转项坐标视图坐标转图形项坐标图形项之间的坐标转换 其他 概述 The Graphics View Coordinate System 图形视图坐标系统是Qt图形视图框架的重要组成部分&#xf…

【win10 win11添加右键】git bash

打开注册表编辑器。 按下Win键 R&#xff0c;然后输入”regedit”并按下回车键来打开注册表编辑器。计算机\HKEY_CLASSES_ROOT\Directory\Background\shell\git_bash\command2. 导航到注册表路径&#xff1a;依次展开”HKEY_CLASSES_ROOT\Directory\Background\shell”。右键…

Git入门(Git快速下载,安装,配置,远程仓库,本地仓库,IDEA提交代码,VScode提交代码使用方案一体)

Git快速下载 通过阿里镜像可以自由挑选版本并快速下载CNPM Binaries Mirrorhttp://npm.taobao.org/mirrors/git-for-windows/ 这里安装最新版本 下载安装文件 安装完后双击文件即可开始安装git 安装 git的安装傻瓜式Next即可 配置 打开git&#xff1a;桌面空白处右击&#…

FTLK-1.3.9的编译和使用

文章目录 1. FTLK-1.3.9源码下载 1. FTLK-1.3.9源码下载 前往FTLK官网下载FTLK-1.3.9源码 *

ARM IHI0069F GIC architecture specification (4)

1.3 支持的配置和兼容性 在 Armv8-A 中&#xff0c;EL2 和 EL3 是可选的&#xff0c;PE 可以支持一个、两个或都不支持这些异常级别。 然而&#xff1a; • PE 要求EL3 支持安全和非安全状态。 • PE 需要EL2 来支持虚拟化。 • 如果未实施EL3&#xff0c;则只有一个安全状态。…