面试官:你说你精通Java并发,给我讲讲 volatile

简介: 大家好,我是指北君。PS:最近又赶上跳槽的高峰期,好多粉丝,都问我要有没有最新面试题,我连日加班好多天,终于整理好了,16000+ 道,295多份,多份面试题大全,我会持续更新中,马上就会整理更多!【文末有领取方式】今天来了解一下面试题:你对 volatile 了解多少。要了解 volatile 关键字,就得从 Java 内存模型开始。最后到 volatile 的原理。

一、Java 内存模型 (JMM)

大家都知道 Java 程序可以做到一次编写然后到处运行。这个功劳要归功于 Java 虚拟机。Java 虚拟机中定义了一种 Jva 内存模型(JMM),用来屏蔽掉各种硬件和操作系统之间内存访问差异,让 Java 程序可以在各个平台中访问变量达到相同的效果。

JMM 的主要目标是定义了程序中变量的访问规则,就是内存中存放和读取变量的一些底层的细节。

JMM 规则

  1. 变量包含实例字段,静态字段,构成数组对象的元素,不包含局部变量和方法参数。
  2. 变量都存储在主内存上。
  3. 每个线程在 CPU 中都有自己的工作内存,工作内存保存了被该线程使用到的变量的主内存副本拷贝。
  4. 线程对变量的所有操作都只能在工作内存,不能直接读写主内存的变量。
  5. 不同线程之间无法之间访问对方工作内存中的变量。

53.jpg

定义一个静态变量: static int a = 1;


54.png

上面的一系列内存操作,在 JMM 中定义了 8 种操作来完成。

JMM 交互

主内存和工作内存之间的交互,JMM 定义了 8 种操作来完成,每个操作都是原子性的。

  1. lock (锁定): 作用于主内存变量,把一个变量标识为一条内存独占的状态。
  2. unlock (解锁): 作用于主内存变量,把 lock 状态的变量释放出来,释放出来后才能被其他线程锁定。
  3. read (读取): 作用于主内存变量,把一个变量的值从主内存传输到工作内存中。
  4. load (载入): 作用于工作内存变量,把 read 操作的变量放入到工作内存副本中。
  5. use (使用): 作用于工作内存变量,把工作内存中的变量的值传递给执行引擎,每当虚拟机遇到需要这个变量的值的字节码指令时都执行这个操作。
  6. assgin (赋值): 作用于工作内存变量,把从执行引擎收到的值赋值给工作内存变量,每当虚拟机遇到需要赋值变量的值的字节码指令时都执行这个操作。
  7. store (存储): 作用于工作内存变量,把工作内存中的一个变量值,传送到主内存。
  8. write (写入): 作用于主内存变量,把 store 操作的从工作内存取到的变量写入主内存变量中。



55.jpg

从上图中可知,JMM 交互在一条线程中是不会出现任何的问题。但是当有两条线程的时候,线程 1 已经修改了变量的值,但是并未刷新到主内存时,如果此时线程 2 读取变量得到的值并不是线程 1 修改过的数据。

当引入线程 2 的时候 定义一个静态变量: static int a = 1;

56.png

下面就可以用 volatile 关键字解决问题。


二、volatile


volatile 可以保证变量对所有线程可见,一条线程修改的值,其他线程对新值可以立即得知。还可以禁止指令的重排序。


可见性

修改内存变量后立刻同步到主内存中,其他的线程立刻得知得益于 Java 的先行发生原则

先行发生原则中的 volatile 原则:一个 volatile 变量的写操作先行于后面发生的这个变量的读操作

定义一个静态变量: static int a = 1;

57.png



可见性原理

对 volatile 修饰的变量,在执行写操作的时候会多出一条 lock 前缀的指令。JVM 将 lock 前缀指令发送给 CPU ,CPU 处理写操作后将最后的值立刻写回主内存,因为有 MESI 缓存一致性协议保证了各个 CPU 的缓存是一致的,所以各个 CPU 缓存都会对总线进行嗅探,本地缓存中的数据是否被别的线程修改了。

如果别的线程修改了共享变量的数据,那么 CPU 就会将本地缓存的变量数据过期掉,然后这个 CPU 上执行的线程在读取共享变量的时候,就会从主内存重新加载最新的数据。

原子性

volatile 并不保证变量具有原子性。58.png这段代码的结果有可能不是 100000,有可能小于 100000。因为 num++ 并不是原子性的。

有序性

volatile 是通过禁止指令重排序来保证有序性。为了优化程序的执行效率 JVM 在编译 Java 代码的时候或者 CPU 在执行 JVM 字节码的时候,不影响最终结果的前提下会对指令进行重新排序。

编译器会根据以下策略将内存屏障插入到指令中,禁止重排序:

  1. 在 volatile 写操作之前插入 StoreStore 屏障。禁止和 StoreStore 屏障之前的普通写操作不会进行重排序。
  2. 在 volatile 写操作之后插入 StoreLoad 屏障。禁止和 StoreLoad 屏障之后的 volatile 读写重排序。
  3. 在 volatile 读操作之后插入 LoadLoad 屏障。禁止和 LoadLoad 之后的普通读和 volatile 读重排序。
  4. 在 volatile 写操作之后插入 LoadStore 屏障。禁止和 LoadStore 屏障之后的普通写操作重排序。

总结

面试被问到 volatile 的时候,可以从 Java 内存模型到原子性、有序性、可见性,最后到 volatile 的原理:内存屏障和 lock 前缀指令。

我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

面试大全包括:包括 Java 集合、JVM、多线程、并发编程、设计模式、SpringBoot、SpringCloud、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、Python、HTML、CSS、Vue、React、JavaScript、Android 大数据、阿里巴巴等大厂面试题等、等技术栈!

领取方式:扫描下方公众号【Java技术指北】回复【006】即可获取


59.jpg

相关文章
|
3月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
79 2
|
16天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
18天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
41 4
|
15天前
|
存储 缓存 Java
大厂面试高频:Volatile 的实现原理 ( 图文详解 )
本文详解Volatile的实现原理(大厂面试高频,建议收藏),涵盖Java内存模型、可见性和有序性,以及Volatile的工作机制和源码案例。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:Volatile 的实现原理 ( 图文详解 )
|
20天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
24天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
46 2
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
29 1
|
1月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
74 2
|
30天前
|
SQL 缓存 安全
[Java]volatile关键字
本文介绍了Java中volatile关键字的原理与应用,涵盖JMM规范、并发编程的三大特性(可见性、原子性、有序性),并通过示例详细解析了volatile如何实现可见性和有序性,以及如何结合synchronized、Lock和AtomicInteger确保原子性,最后讨论了volatile在单例模式中的经典应用。
39 0
|
2月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。

热门文章

最新文章

下一篇
无影云桌面