首页 > 其他分享 >面试官:volatile如何保证可见性的,具体如何实现?

面试官:volatile如何保证可见性的,具体如何实现?

时间:2024-03-19 09:15:16浏览次数:26  
标签:面试官 缓存 变量 stop 如何 线程 volatile CPU

写在开头

在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码块,方法参数,局部变量以及实例常量,类常量多不能进行修饰。

自JDK1.5之后,官网对volatile进行了语义增强,这让它在Java多线程领域越发重要!因此,我们今天就抽一晚上时间,来学一学这个关键字,首先,我们从标题入手,思考这样的一个问题:

volatile如何保证可见性,具体如何实现的?

带着疑问,我们继续往下阅读!

volatile如何保证可见性

volatile保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了共享变量的值,共享变量修改后的值对其他线程立即可见。

我们先通过之前写的一个小案例来感受一下什么是可见性问题:

【代码示例1】

public class Test {
    //是否停止 变量
    private static boolean stop = false;
    public static void main(String[] args) throws InterruptedException {
        //启动线程 1,当 stop 为 true,结束循环
        new Thread(() -> {
            System.out.println("线程 1 正在运行...");
            while (!stop) ;
            System.out.println("线程 1 终止");
        }).start();
        //休眠 1 秒
        Thread.sleep(1000);
        //启动线程 2, 设置 stop = true
        new Thread(() -> {
            System.out.println("线程 2 正在运行...");
            stop = true;
            System.out.println("设置 stop 变量为 true.");
        }).start();
    }
}

输出:

线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.

原因:
我们会发现,线程1运行起来后,休眠1秒,启动线程2,可即便线程2把stop设置为true了,线程1仍然没有停止,这个就是因为 CPU 缓存导致的可见性导致的问题。线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。
image

那这个问题怎么解决呢?很好解决!我们排volatile上场可以秒搞定,只需要给stop变量加上volatile修饰符即可!

【代码示例2】

//给stop变量增加volatile修饰符
private static volatile boolean stop = false;

输出:

线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.
线程 1 终止

从结果中看,线程1成功的读取到了线程而设置为true的stop变量值,解决了可见性问题。那volatile到底是什么让变量在多个线程之间保持可见性的呢?请看下图!
image

如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取,具体实现可总结为5步。

  • 1️⃣在生成最低成汇编指令时,对volatile修饰的共享变量写操作增加Lock前缀指令,Lock 前缀的指令会引起 CPU 缓存写回内存;
  • 2️⃣CPU 的缓存回写到内存会导致其他 CPU 缓存了该内存地址的数据无效;
  • 3️⃣volatile 变量通过缓存一致性协议保证每个线程获得最新值;
  • 4️⃣缓存一致性协议保证每个 CPU 通过嗅探在总线上传播的数据来检查自己缓存的值是不是修改;
  • 5️⃣当 CPU 发现自己缓存行对应的内存地址被修改,会将当前 CPU 的缓存行设置成无效状态,重新从内存中把数据读到 CPU 缓存。

总结

其实volatile关键字不仅仅能解决可见性问题,还可以通过禁止编译器、CPU 指令重排序和部分 happens-before 规则,解决有序性问题,我们放在下一篇聊。

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

image

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

image

标签:面试官,缓存,变量,stop,如何,线程,volatile,CPU
From: https://www.cnblogs.com/JavaBuild/p/18081962

相关文章

  • 自然语言:如何通过机器学习和自然语言处理技术训练生成一个AI?有没有代码展示
    要通过机器学习和自然语言处理技术训练生成一个AI,有一些步骤和代码示例可以帮助你。1.数据收集和准备:首先,需要收集和准备用于训练的数据。这可以是文本数据集,例如新闻文章、电影评论等。确保数据集的质量和多样性。2.数据清洗和预处理:清洗和预处理数据是一个重要的步骤。这可......
  • 当我需要实现某个外部属性变化,更新表格的某一列,所有值均为变化后的值,应该如何实现
    在这里,将tableData添加到useEffect的依赖数组会导致无限循环。因为在useEffect内部更新了tableData状态,每次状态改变又会触发useEffect再次执行,形成无限循环。解决这个问题的一种方法是,在状态更新时创建一个新的数组,而不是直接修改现有数组。这样就不会触发依赖数组中tableData的......
  • UEFI|电脑Windows系统如何迁移到新安装的硬盘?系统迁移详细教程!
    前面讲了很多很多关于安装系统、重装系统的教程。但唯独没有讲到电脑换了新的硬盘之后,怎么把旧系统迁移到新的硬盘上。今天小白就来跟各位小伙伴详细唠唠:开始之前需要把系统迁移的条件准备好,意思就是在WinPE系统下,可以正常且同时访问新硬盘和旧系统盘。点击下方蓝字查看系......
  • 想要干扰源得到有效控制,金属外壳与电路板该如何良好接地?
    电子产品接地问题是一个老生常谈的话题,文章来聊聊其中一小部分,主要内容是金属外壳与电路板的接地问题。我们经常会看到一些系统设计中将PCB板的地(GND)与金属外壳(EGND)之间通常使用一个高压电容C1(1~100nF/2KV)并联一个大电阻R1(1M)连接。那么为什么这么设计呢?原理图示意......
  • 常用命令--如何防止黑客修改文件--chattr
    常用命令--如何防止黑客修改文件--chattrchattrchattr命令用来改变文件属性。这项指令可改变存放在ext2文件系统上的文件或目录属性,这些属性共有以下8种模式:a:让文件或目录仅供附加用途;b:不更新文件或目录的最后存取时间;c:将文件或目录压缩后存放;d:将文件或目录排除在倾倒操作......
  • Java中的多线程是如何实现的?
    ​​​​​​继承Thread类:通过继承Java的Thread类并重写其run()方法,可以创建一个线程。run()方法包含了线程要执行的代码。创建Thread子类的实例,并调用其start()方法来启动线程。start()方法会导致线程开始执行,自动调用run()方法。注意:Java不支持多重继承,因此如果类已经继承了......
  • Java中的泛型是如何工作的?
    Java中的泛型(Generics)是JDK5.0引入的一个新特性,它提供了编译时类型安全,允许在定义类、接口和方法时使用类型参数(typeparameters)。泛型的主要目的是在编译时增强类型检查,以减少运行时类型转换的错误,同时保持使用泛型类型时的类型灵活性。以下是Java中泛型工作的几个关键点:类......
  • 如何突破DRAM对SSD容量提升的限制?
    近日小编看到PureStorage公司的研发高级副总裁肖恩·罗斯马林(ShawnRosemarin)的一个观点“由于DRAM的局限性,固态硬盘(SSD)的容量难以突破30TB”。这个观点不是完全准确,实际上,Solidigm已经发布了最大容量61.44TBQLCSSD。但是,这个观点背后的逻辑依然是业内在提升SSD容......
  • 零基础小白如何入门HarmonyOS鸿蒙应用开发学习?
    HarmonyOS鸿蒙应用开发是当前非常热门的一个领域,许多人都想入门学习这个技术。但是,对于零基础的人来说,如何入门确实是一个问题。下面,我将从以下几个方面来介绍如何零基础入门HarmonyOS鸿蒙应用开发学习。一、了解HarmonyOS鸿蒙系统首先,我们需要了解HarmonyOS鸿蒙系统的一些......
  • 【操作系统】线程、程序、进程死锁的必要条件?如何避免死锁?死锁的预防,死锁的避免(银行
    目录线程、程序、进程死锁的必要条件?如何避免死锁?死锁的预防死锁的避免(银行家)死锁的检测进程-资源分配图死锁检测步骤死锁的解除线程、程序、进程进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行......