《面试1v1》volatile

news2024/11/25 12:01:24

基本功

我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。

面试官: 你能解释一下 volatile 关键字的作用吗?

候选人: 当我们在编写多线程程序时,经常会遇到线程安全的问题。其中一个常见的问题是可见性问题,即一个线程修改了共享变量的值,但是其他线程并不能立即看到这个修改。这时候,我们可以使用 volatile 关键字来解决这个问题。

面试官: 非常好。那么,你能具体说明一下 volatile 关键字是如何保证可见性的吗?

候选人: 当一个变量被声明为 volatile 后,每次访问这个变量时,都会从内存中读取最新的值,而不是使用 CPU 缓存中的旧值。同样地,每次修改这个变量时,都会立即将新值写入内存,而不是等到线程结束或者 CPU 缓存刷新时才写入。这样,其他线程就可以立即看到这个变量的最新值,从而保证了可见性。

在 JVM 中,volatile 关键字的实现涉及到以下几个方面:

  1. 内存屏障:JVM 会在 volatile 变量的读写操作前后插入内存屏障,以保证指令不会被重排序。内存屏障可以分为读屏障、写屏障和全屏障,分别用于保证读操作、写操作和所有操作的有序性。下面是 HotSpot JVM 中的 volatile 内存屏障实现:
inline void OrderAccess::fence() {
  __asm__ volatile ("" : : : "memory");
}

inline void OrderAccess::loadload() {
  __asm__ volatile ("lfence" : : : "memory");
}

inline void OrderAccess::storestore() {
  __asm__ volatile ("sfence" : : : "memory");
}

inline void OrderAccess::loadstore() {
  __asm__ volatile ("mfence" : : : "memory");
}

inline void OrderAccess::storeload() {
  __asm__ volatile ("mfence" : : : "memory");
}
  1. 内存语义:JVM 的内存模型规定了共享变量的访问方式,以及如何保证可见性和有序性。对于 volatile 变量,JVM 会保证每次读取都从内存中读取最新的值,每次写入都立即写入内存,以保证可见性和有序性。下面是 HotSpot JVM 中的 volatile 内存语义实现:
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest)
                    , "m" (*dest)
                    : "cc", "memory");
  return exchange_value;
}

inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) {
  __asm__ volatile (LOCK_IF_MP(%4) "cmpxchg8b (%3)"
                    : "=A" (exchange_value)
                    : "b" ((jint)exchange_value), "c" ((jint)(exchange_value >> 32)), "r" (dest)
                    , "m" (*dest)
                    : "cc", "memory");
  return exchange_value;
}
  1. 编译器优化:JVM 的编译器会对代码进行优化,以提高程序的性能。但是,对于 volatile 变量,编译器会禁止一些优化,以保证指令不会被重排序。比如,编译器不会将 volatile 变量的读写操作与其他指令重排序,也不会将 volatile 变量的读操作和写操作合并为一个操作。下面是 HotSpot JVM 中的 volatile 变量读写操作的实现:
inline jint    Atomic::load    (volatile jint*    p) { return *p; }
inline jlong   Atomic::load    (volatile jlong*   p) { return *p; }
inline jfloat  Atomic::load    (volatile jfloat*  p) { return *p; }
inline jdouble Atomic::load    (volatile jdouble* p) { return *p; }

inline void    Atomic::store   (volatile jint*    p, jint    x) { *p = x; }
inline void    Atomic::store   (volatile jlong*   p, jlong   x) { *p = x; }
inline void    Atomic::store   (volatile jfloat*  p, jfloat  x) { *p = x; }
inline void    Atomic::store   (volatile jdouble* p, jdouble x) { *p = x; }

面试官: 很好。那么,你能否举一个例子来说明 volatile 关键字的作用呢?

候选人: 当然。比如,我们可以定义一个 flag 变量,并在一个线程中修改它的值,然后在另一个线程中读取它的值。如果 flag 变量没有被声明为 volatile,那么在另一个线程中读取 flag 变量的值时,可能会看到旧值,而不是最新的值。但是,如果 flag 变量被声明为 volatile,那么在另一个线程中读取 flag 变量的值时,就可以保证看到最新的值。

下面是一个简单的示例代码,演示了 volatile 关键字的作用:

public class VolatileExample {
    private volatile boolean flag = false;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public void doSomething() {
        while (!flag) {
            // do something
        }
        // do something else
    }
}

在这个示例中,我们定义了一个 VolatileExample 类,其中包含一个 flag 变量。在 doSomething() 方法中,我们使用了一个 while 循环来等待 flag 变量的值变为 true。如果 flag 变量没有被声明为 volatile,那么在另一个线程中调用 setFlag(true) 方法后,doSomething() 方法可能会一直等待下去,因为它看不到 flag 变量的修改。但是,由于 flag 变量被声明为 volatile,所以在另一个线程中调用 setFlag(true) 方法后,doSomething() 方法会立即看到 flag 变量的修改,从而退出循环。

面试官: 非常好。那么,你认为 volatile 关键字有什么缺点吗?

候选人: volatile 关键字只能保证可见性,不能保证原子性。如果一个变量的修改涉及到多个步骤,那么使用 volatile 关键字可能会导致线程安全问题。在这种情况下,我们需要使用其他的同步机制,比如 synchronized 关键字或者 Lock 接口。

面试官: 很好。你对 volatile 关键字的理解非常清晰。部分是比较考验工程师基本功的,你回答的很好,这部分可以过了。

候选人: 非常感谢。

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!

🎁目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

http://javapub.net.cn

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

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

相关文章

iptables防火墙(一)

iptables防火墙 一、iptables概述1、netfilter 与 iptables 的关系1.netfilter2.iptables 2、四表五链1.四表2.五链3.表的匹配优先级4.规则链之间的匹配顺序5.规则链内的匹配顺序 二、iptables防火墙的安装及配置方法1、iptables防火墙安装2、iptables防火墙的配置方法1.iptabl…

nginx(七十八)日志的深入探究

一 日志 ① nginx与日志相关的指令 access_log log_format error_log rewrite_log log_subrequest debug_connection rewrite_log limit_conn_log_level limit_req_log_level log_not_found open_log_file_cache uninitialized_variable_warn log_not_found …

【重新定义matlab强大系列八】利用matlab求局部值(函数islocalmax求局部最大值+函数islocalmin求局部最小值)

🔗 运行环境:Matlab 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 #### 防伪水印——左手の明天 #### 💗 大家好🤗&#x1f91…

三控开关接线方式记录

参考原视频 三控开关与双控开关的多种接法,多控开关的工作原理_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Qh4y1J7gC/?spm_id_from333.337.search-card.all.click&vd_sourcee821a225c7ba4a7b85e5aa6d013ac92e原视频讲的不错,大家可以关…

国内好用的免费AI处理工具:Chat8(实现类似ChatGPT功能、TCP/IP通讯问题)

目前国外的ChatGPT比较火,朋友推荐了一个chat8,经过使用,觉得还不错,链接如下: https://ai.chat86.co/go/kl/775283,进去后可以直接用手机注册使用,以下是我问其关于TCP/IP的对话过程&#xff0…

(转载)从0开始学matlab(第11天)—关系运算符和逻辑运算符

选择结构的运算由一个表达式控制的,这个表达式的结果只有 true(1) 和 false(0)。有两种形式的运算符可以在 MATLAB 中关系得到 true/false:关系运算符和逻辑运算符。跟 C 语言一样, MATLAB 没有布尔型和逻辑数据类型。 MATLAB 把 0 …

模块一:k8s集群部署与安全配置

模块一:k8s集群部署与安全配置 目录 1、K8s安全运维概述 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PST6trat-1684674016197)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/1665822875941-73d822c8-7bdd-481c-acc1-df97b70c…

python爬虫实战——小说爬取

python爬虫实战——小说爬取 基于requests库和lxml库编写的爬虫,目标小说网站域名http://www.365kk.cc/,类似的小说网站殊途同归,均可采用本文方法爬取。 目标网站:传送门 本文的目标书籍:《我的师兄实在太稳健了》…

软件设计师第4题

首先,我是备考2023年上半年的考试。 一、历年考试题 历年的考题如下,从表中分析可以看出,动态规划法、排序算法、回溯法、分治法是很大概率考察的算法,尤其是动态规划法,本身其理解难度较高,且可以出的题型…

docker:容器的数据卷

1 数据卷概念及作用 1.1 什么是容器数据卷 先来看看Docker的理念: 将应用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的容器之间希望有可能共享数据 Docker容器产生的数据,如果不通过…

什么是人工智能的知识图谱?知识图谱的组成、构建、应用有哪些?

人工智能(Artificial Intelligence,AI)是一种通过计算机模拟人类智能的技术,其应用范围越来越广泛。知识图谱(Knowledge Graph,KG)则是人工智能技术中的重要组成部分,它是一种结构化…

Redis集群安装之主从集群

1.主从集群 Redis有三种集群模式,分别是:主从模式、哨兵模式、Cluster模式。Rdis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和s…

TCP实现HTTP服务

在之前的篇章中我们已经讲过七层参考模型了,今天我们从传输层实现应用层http服务 使用nodejs原生net模块就可以打通TCP传输层并且提供一个端口号进行监听 创建一个TCP服务 import net from netconst server net.createServer((socket) > {socket.on(data, (da…

【路径规划】基于人工蜂群算法的栅格法路径规划 机器人路径规划【Matlab代码#23】

文章目录 【可更换其他算法,获取资源请见文章第6节:资源获取】1. 原始ABC算法2. 机器人路径规划环境创建3. 路径规划模型建立4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法,获取资源请见文章第6节:资源获取】 1. 原…

【实时性】实时性优化的一些参数设置和心得

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

Deformable DETR 论文学习

1. 解决了什么问题? DETR 去除了目标检测算法中的人为设计,取得了不错的表现。但是其收敛速度很慢,对低分辨率特征识别效果差: 模型初始化时,注意力模块给特征图上所有的像素点分配的权重是均匀的,就需要…

chatgpt赋能Python-python5的阶乘

Python5的阶乘介绍 Python是一门广泛应用于编写脚本、自动化、爬虫、数据分析等方面的编程语言,也是很多科研领域使用的首选。Python的功能和灵活性可以帮助用户解决各种问题,而本文要介绍的是Python中计算阶乘的方法。 阶乘是数学中的一个概念&#x…

fullter 学习记录_01_插件整理

flutter学习记录第一节--搭建项目及路由的设置 1.轮播图: flutter_swiper1.1 用处1.2 导入flutter_swiper库1.3 导入库,运行后可能遇到的问题1.4 属性说明1.5 代码案例 2. flutter_screenutil2.1 用处2.2 引用2.3 使用说明2.4 代码实现按理2.5 ScreenUtl 的封装 1.轮…

UNIX环境高级编程——守护进程

13.1 引言 守护进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。 13.2 守护进程的特征 系统进程依赖于操作系统实现。父进程ID为0的各…

xxs跨站之原理分类及攻击手法

xss跨站达到原理&#xff0c;危害和特点 他和语言没有太大关系&#xff0c;它大部分都是属于一个前端的漏洞&#xff0c;搭建一个简易的php网站存在xss跨站漏洞 访问这个网站&#xff0c;x1&#xff0c;就输出1&#xff0c; 如果我们把x<script>alert(1)</script&g…