面试官:说说volatile应用和实现原理?

volatile 是并发编程中的重要关键字,它的名气甚至是可以与 synchronized、ReentrantLock 等齐名,也是属于并发编程五杰之一。

需要注意的是 volatile 并不能保证原子性,因此使用 volatile 并没有办法保证线程安全

并发编程五杰:

alt

PS:“并发编程五杰”是我个人起的名字,大家也不用太当真。

1.什么是 volatile?

volatile 是 Java 中的一个关键字,用于修饰变量,它的主要作用是保证变量的可见性和禁止指令重排序

  • 可见性:是指当一个线程修改了一个被 volatile 修饰的变量时,其他线程能够立即看到这个修改。
  • 禁止指令重排序:则是确保对 volatile 变量的读写操作不会被编译器或处理器随意重新排序,从而保证了程序执行的顺序符合我们的预期。

2.volatile 工作原理

为了实现可见性,Java 内存模型(JMM)会在对 volatile 变量进行写操作时,强制将工作内存中的值刷新到主内存,并在读取时强制从主内存中重新获取最新的值。

而禁止指令重排序是通过在编译器和处理器层面添加特定的内存屏障指令来实现的。

具体来说。

2.1 可见性实现原理

可见性:在计算机编程特别是多线程编程中,“可见性”指的是一个线程对共享变量的修改,对于其他线程是否能够及时地、准确地“可见”,即其他线程是否能够及时感知到这个修改并获取到最新的值。

例如,在一个多线程环境中,如果线程 A 修改了一个共享变量的值,而线程 B 无法立即看到这个修改,那么就存在可见性问题。

多线程操作共享变量流程如下:

alt

volatile 是通过内存屏障(Memory Barrier) 来确保可见性。

  • 写屏障(Store Barrier):在 volatile 变量的写操作之后插入写屏障,确保所有之前的写操作都同步到主内存中,从而使得其他线程在读取该变量时能够获取到最新的值。
  • 读屏障(Load Barrier):在 volatile 变量的读操作之前插入读屏障,确保所有之前的写操作都已完成,从而读取到的是最新的值。

通过这种方式,volatile 变量在多线程环境下的读写操作能够保持较高的可见性,但需要注意的是,volatile 并不保证操作的原子性。

具体来说,volatile 内存可见性主要通过 lock 前缀指令实现的,它会锁定当前内存区域的缓存(缓存行),并且立即将当前缓存行数据写入主内存(耗时非常短),回写主内存的时候会通知其他线程缓存了该变量的地址失效,从而导致其他线程需要重新去主内存中重新读取数据到其工作线程中。

2.2 有序性实现原理

volatile 的有序性是通过插入内存屏障,在内存屏障前后禁止重排序优化,以此实现有序性的。

2.3 正确理解“内存屏障”?

volatile 保证可见性的“内存屏障”和保证有序性的“内存屏障”有什么区别呢?

在说它们的区别之前,我们现需要对“内存屏障”有一个大致的理解。

内存屏障,简单来说,就像是在内存操作中的一道“关卡”或者“栅栏”。

想象一下,计算机在执行程序的时候,为了提高效率,可能会对指令的执行顺序进行一些调整。但是在多线程或者多核心的环境下,这种随意的调整可能会导致一些问题。

内存屏障的作用就是阻止这种随意的调整,确保特定的内存操作按照我们期望的顺序执行。

所以“内存屏障”本身只是一种“技术”,而这种“技术”可以实现很多“业务功能”。

这就像 Spring 中的 AOP 一样,AOP 是一种“技术”,而这种技术可以实现很多业务功能。例如,针对日志处理可以使用 AOP、针对用户鉴权可以使用 AOP 等,而内存屏障也是一样,我们可以使用内存屏障实现可见性的“业务功能”,也可以实现有序性的“业务功能”等。

3.volatile 适用场景

volatile 常见场景有以下两种:

  1. 状态标记
  2. 单例模式中的双重检查锁

具体来说。

3.1 状态标记

例如,在多线程环境中用于表示某个任务是否完成的标志变量,具体代码如下:

volatile boolean isTaskFinished = false;

3.2 单例模式中的双重检查锁

class Singleton {
    private volatile static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

4.volatile 局限性

volatile 并不能保证原子性,也就是并不能保证线程安全

例如,对于 i++ 这样的操作,它不是一个原子操作,单纯使用 volatile 修饰 i 并不能保证线程安全。

课后思考

为什么双重效验锁一定要加 volatile?不是已经加锁了吗?

#java##八股文#
全部评论

相关推荐

10-08 19:22
已编辑
门头沟学院 Java
#软件开发笔面经#timeline 2024/9/8号1.自我介绍2.java常见集合有哪些3.LinkedList和ArrayList的区别,(适用场景还需深究?)4.hashMap底层了解吗(加入红黑树的原因有哪些,除了查询速度)5.hashMap默认因子,默认长度,以及扩容时候会不会再哈希6.ThreadLocal了解吗,以及原理7.线程池如果在持续工作,下一个进来的线程会拿到上一个线程的数据吗8.常用的线程池有哪些9.线程池的几大参数,以及抛弃策略具体有哪些10.了解Mysql索引吗11.场景题:A,B,C创建联合索引, where A=“”and C="" and  B="",会怎么走索引(这个按道理不会走全部索引,当时mysql自己会进行优化,所以会走全部索引!)12.forUpdate 和show lock on .....是什么(锁那一块)13.Mysql如何对一行数据加锁14.explain了解吗15.实习的时候有慢sql的优化经验16.分页处理  1.select .. limit a,b 和主键索引加子查询,什么情况某个优势更显著(有瓶颈)17.mysql事务隔离级别18.linux常见命令,知道linux里面文件是有用户和用户组的概念吗19.什么命令怎么一下创建多件不存在的文件夹20.拥塞控制了解吗21.http的常见状态码22.三次握手,四次挥手了解吗,以及每个阶段的状态名称比如什么wait_establish23.握手过程,一方最多等待多久(两倍的报文最大数)24.tcp,http都分别处在那一层25.jvm垃圾回收算法有哪些26.常见的垃圾回收器27.CMS和G1的区别28.讲讲spring的ioc和aop,你们一般会aop干嘛29.切点和切面有什么区别30.@AutoWird是如何加载一个类的31.Bean的生命周期32.spring的单例Bean是线程安全的吗33.redis的淘汰策略34.如何解决缓存雪崩35.算法题:(秒了!)给定一个已排序的链表的头head, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表1->2->2->3->3->4->5 1->4->5反问:多久出结果公司项目是什么,框架是什么建议,评价hhhhh说八股背的挺6,难绷,还是建议多一点自己的理解当场约二面了 学到了一些东西,比如16点和11点  还需改进的:6,7,12,13,18,19,20,22,23,27,30二面 自我介绍实现拷打RabbitMQ了解吗,优点,缺点如何保证MQ的数据可靠性分布式的事务一致性如何保证Spring了解吗说说看,springboot了解吗Springboot中的核心注解了解吗Yml,yaml,proprities了解吗如何学习一个新的技术栈未来规划操作系统:父子进程如何切换(寄)操作系统:线程组了解吗(寄)#软件开发笔面经#
点赞 评论 收藏
分享
头像
10-23 11:09
已编辑
上海交通大学 Java
纪念并记录下秋招第一个二面,发发面经,攒人品,许愿oc!时间线:9.18号投递9.20号笔试+AI面试,3道编程题A了2道10.9号一面,应该是被某部门捞了,但也是后端开发10.12号二面10.17号hr面10.23发现回人才库(罢了罢了)一面:自我介绍你的研究方面和岗位不是很匹配,怎么看详细描述浏览器输入网址到看到网页的过程刚刚提到的HTTP协议属于OSI的第几层,是基于什么协议的介绍TCP协议的三次握手TCP协议用哪些方式保证传输信息的可靠性什么是TCP的拥塞控制(慢启动、拥塞避免、快重传、快恢复)TCP在什么情况下判断数据包丢失,触发快重传(发送方连续收到3个相同的ACK)TCP流量控制的算法(滑动窗口)除了窗口,还有哪些限流算法(当时没回答出来,复盘时想起应该还有令牌桶算法和漏桶算法)HTTP和HTTPS请求及协议的区别HTTPS用到的安全协议是什么使用的加密算法是对称加密和还是非对称(当时回答非对称,复盘时想起应该是在证书验证阶段使用非对称,后面的数据传输为对称加密)私钥谁保管进程与线程的区别知道协程吗,协程与进行、线程的区别进程的基本状态进程间的通信方式Linux下的IO模型(当时只回答了BIO和NIO)除了BIO和NIO,你知道还有多路复用吗你知道Linux下的系统调用吗(不清楚,面试官补充是select、poll这种,但是确实没准备到)Spring的IOC是什么Spring对象是线程安全的吗?(回答,Spring的bean一般是单例的,单例的无状态的bean是线程安全的,如果是有状态的,即有可变的成员变量,就不是线程安全的)如何解决有状态的bean的线程安全问题?(简单方法将可变的成员用final修饰变为不可变,但是推荐使用ThreadLocal保存可变成员变量)有什么办法定义多例bean介绍ThreadLocal底层数据结构父线程定义的TreadLocal如何在子线程获取到(这个不会,瞎说的,面试官有引导,但是依然没说对)子线程中把某个变量清理掉,父线程还能拿到那个变量吗(这个也不会)线程池的重要参数核心线程是否会被回收(不会被回收)拒绝策略有哪些抢购场景如何设计线程池(瞎说的)锁的可重入性公平锁和非公平锁的区别MySOL的事务隔离级别Spring如何实现事务如何使用AOP技术,简单地说就是把两个插入同时放到一个事务中你知道RPC吗(不知道)你知道CAP吗(不知道,RPC和CAP都没准备到,应该是Spring cloud中的,后面反问的时候面试官也提到对Spring cloud相关的一套东西都不了解)手撕:选择数组中第K个大的元素,我直接Array.sort,然后输出k-1过了反问:多久可以收到下一次面试通知面试官提出一个我的问题:只了解基础的东西不够,实际工作中基础体现得不是很明显,大部分还是实际使用的中间件(确实,没有实习,实践经历是硬伤啊!)工作地是否有问题因为手撕太简单了而且笔试过了好久才有的一面,部门也跟投递时不同明显被捞,我以为是kpi面,但是上午面完,下午就收到了二面。二面:聊项目(一个大模型相关的,跟后端开发没有关系,但是因为跟毕设相关很熟,自我感觉回答得不错,第一次二面觉得很神奇)手撕:leetcode026重排列表反问:还有几面部门业务上午面的,中午收到HR面10.17 HR面一些比较基础的问题等待oc! (等个鬼 10.23回人才库) #美团# #美团求职进展汇总# #美团面经#
点赞 评论 收藏
分享
10-12 11:52
已编辑
陕西理工大学 Java
四个环节:1.八股 2.项目 3.个人相关 4.介绍公司相关1.转发与重定向区别url访问网址的全过程讲一下反射有没有适合使用反射的场景以设计的思想去想反射适用于哪些场景讲一下对HashMap原理理解对于Map这种数据结构适用的场景垃圾回收的目的是什么,处理了什么问题讲一下G1讲一下线程池线程核心参数线程参数中空闲队列的作用抽象的说一下 锁是在什么情况下适用于什么场景解决什么问题你会在什么情况下选择使用分布式锁,什么情况下使用单体单机锁?你最终选择的判断标准是什么?2.项目缓存怎么做的?方案是怎么做的?从缓存框架来看有什么优化空间?(不会,面试官说可以做多级缓存)缓存做的时候有没有考虑过会出现哪些问题?(面试官引导问 缓存内存相关)做缓存的时候有没有考虑过兜底方案?(面试官说,考虑自动化巡检,预警)限流接口如何设计3.哪些渠道学习学习方式有什么逛GitHub有没有感兴趣的东西有没有研究过大模型更高级的用法(面试官说Agent)自己最近做的项目什么的想发展的方向,未来的规划4.反问,感觉问不下去了,面试官很好,说想问就问。问了培养计划,多久可以参与开发工作。,背了几天的八股,没有对上的/(ㄒoㄒ)/~~。我也想八股吟唱。面试官很好,全程不会的就会给我提示以及给出自己的问题解决方案。好公司不知道,这个面试官很开朗人很好。基本上寄了,希望牛油们能早日拿offer#数字马力#更新——————竟然进复试了,很多问题感觉都没咋答出来,后面的问题感觉是面试官给我讲了很多。这一次我一定要把握机会。大家都要加油。
点赞 评论 收藏
分享
8 35 评论
分享
牛客网
牛客企业服务