面试官:为什么需要happens-before规则和什么是指令重排序

三:深入理解 synchronized 关键字 count = 0;_waiters = 0,//等待线程数//重入次数//持有锁的线程(逻辑上,实际上除了THREAD,还可能是Lock Record)//线程wait之后会进入该列表//等待获取锁的线程列表,和_EntryList配合使用//等待获取锁的线程列表,和_cxq配合使用//当前持有者是否为THREAD类型,如果是轻量级锁膨胀而来,还没有enter的话,//_owner存储的可能会是Lock Record当多个线程同时访问一段同步代码时,首先会进入。 阅读详情

本篇文章从happens-before定义、用途以及具体规则三个方面对happens-before进行解读,并通过源码案例深入了解为什么需要happens-before规则和什么是指令重排序。

什么是happens-before

happens-before: A happens-before B就是A先行发生于B(这种说法不是很准确),定义为hb(A, B)。在Java内存模型中,happens-before的意思是前一个操作的结果可以被后续操作获取。

为什么需要happens-before

JVM会对代码进行编译优化,会出现指令重排序情况,为了避免编译优化对并发编程安全性的影响,需要happens-before规则定义一些禁止编译优化的场景,保证并发编程的正确性。以双重检查单例示例进行分析:

/**
 * 懒汉式 双重检查锁实现单例
 */
public class LazyDoubleCheckSingleton {

    private static LazyDoubleCheckSingleton instance = null;

    private LazyDoubleCheckSingleton(){}

    /**
     * 双重检查锁 减小加锁粒度  相对于LazySynchronizedSingleton提升性能 减少不必要的锁开销
     * @return
     */
    public static LazyDoubleCheckSingleton getInstance(){
        if (instance == null){
            synchronized (LazyDoubleCheckSingleton.class){
                if (instance == null){
                     //CPU执行时候会转换成JVM指令执行
                    //1.分配内存给对象 2.初始化对象 3.将初始化对象和内存地址建立关联,赋值 
                    //4.用户初次访问
                    //存在问题指令重排序 有可能2 3顺序颠倒
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

手机版源码图片

上述代码中instance = new LazyDoubleCheckSingleton()并不是原子操作 ,JVM会分解成以下几个命令执行:

  1. 给对象分配内容
  2. 初始化对象
  3. 将初始化对象和内存地址建立关联

按照上面的分解顺序(1->2->3)执行不存在任何问题,但是由于JVM编译优化的存在,可能导致2和3步骤颠倒,即按1->3->2顺序执行(这就是指令重排序)。按照1->3->2顺序执行,在多线程环境中执行getInstance就有可能出现instance已经和初始对象内存建立关联,但是对象还没有初始化完成的情况,即执行if (instance == null)的时候instance != null 直接返回没有初始化完成的instance,导致再使用instance实例的时候报错。volatile关键字是可以解决指令重排序问题的一种方式,具体解决方式如下:

//添加volatile解决指令重排序问题
private volatile static LazyDoubleCheckSingleton instance = null;

有哪些happens-before规则

  • 程序次序规则: 在一个线程内一段代码的执行结果是有序的。就是还会指令重排,但是随便它怎么排,结果是按照我们代码的顺序生成的不会变。
  • 管程锁定规则:就是无论是在单线程环境还是多线程环境,对于同一个锁来说,一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果!(管程是一种通用的同步原语,synchronized就是管程的实现)
  • volatile变量规则:就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
  • 线程启动规则:在主线程A执行过程中,启动子线程B,那么线程A在启动子线程B之前对共享变量的修改结果对线程B可见。
  • 线程终止规则:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见。也称线程join()规则。
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()检测到是否发生中断。
  • 传递性规则:这个简单的,就是happens-before原则具有传递性,即hb(A, B) , hb(B, C),那么hb(A, C)。
  • 对象终结规则:这个也简单的,就是一个对象的初始化的完成,也就是构造函数执行的结束一定 happens-before它的finalize()方法。

END

笔者是一位热爱互联网、热爱互联网技术、热于分享的年轻人,如果您跟我一样,我愿意成为您的朋友,分享每一个有价值的知识给您。喜欢作者的同学,点赞+转发+关注哦!

点赞+转发+关注,添加小助手VX:xuanwo008即可获得BAT大厂面试资料、高级架构师VIP视频课程等高质量技术资料。

BAT等一线互联网面试资料和VIP高级架构师视频

 

C# 中 [MethodImpl(MethodImplOptions.Synchronized)] 的使用详解 阅读详情

相关推荐

Java面试必考!揭秘as-if-serial与happens-before的关键区别

as-if-serial规则的核心思想是,尽管JVM允许一定的指令重排序,但所有重排序操作必须确保程序的外部行为(特别是对外部可见的变量)是可以“仿佛串行执行”的,即“在多线程并发的情况下,仍然保留串行执行的效果。

孤芳不自赏 912

synchronized关键字

synchronized的总结

m0_67683346的博客 1562

java面试必问6:volatile 关键字:从可见性到指令重排序,一文讲透

为了提高性能,编译器和 CPU 可能会改变代码的执行顺序,只要不影响单线程的最终结果。int a = 1;// 语句1int b = 2;// 语句2a = a + 1;// 语句3实际执行可能是 2 → 1 → 3,因为语句1和语句2没有依赖关系。要点内容核心作用保证可见性、禁止指令重排序底层机制内存屏障 + 缓存一致性协议(MESI)原子性不保证复合操作,不能替代锁典型场景状态标记量、双重校验锁单例性能比锁轻量,但比普通变量稍慢一句话记住 volatile。

zmsofts的博客 364

C#多线程 同步方法synchronized

http://www.what21.com/sys/view/csharp-net_11_1473746333228.html

dhsajkd的专栏 2253

揭秘Java内存模型:指令重排序的那些事儿

指令重排序,顾名思义,就是指处理器在执行程序指令时,改变了原本顺序的行为。这种现象看似奇怪,但在大多数情况下,它对程序员的影响是微乎其微的,尤其是在单线程环境下。可是,重排序在多线程环境中,却会带来非常大的困扰。1. 处理器层面的重排序我们先从硬件层面来了解一下重排序。现代的处理器为了提高性能,采用了指令流水线技术。所谓流水线,就是将多个指令的执行分成多个阶段,同时执行,从而提高处理器的执行效率。为了让指令能够更高效地执行,处理器会尝试对指令的执行顺序进行一定的调整,这就是指令重排序。

孤芳不自赏 627

什么是指令重排序?指令重排序可能导致的问题是什么?指令重排序实际执行的指令步骤是怎么样的?指令重排序遵守的规则有哪些?as-if-serial规则和happens-before规则的区别?

指令重排序是现代处理器为了提高性能而采取的一种优化技术。在实际执行中,指令重排序可能会改变代码中指令的执行顺序,以便更好地利用处理器的资源,加速代码的执行。然而,重排序的结果必须保证最终的执行结果与原来的执行顺序一致。

小小鱼儿小小林的博客 6007

阿里面试题:Java 并发编程之 happens-before 规则

机会是留给有准备的人,大家在求职之前应该要明确自己的态度,熟悉求职流程,做好充分的准备,把一些可预见的事情做好。对于应届毕业生来说,校招更适合你们,因为绝大部分都不会有工作经验,企业也不会有工作经验的需求。同时,你也不需要伪造高大上的实战经验,以此让自己的简历能够脱颖而出,反倒会让面试官有所怀疑。你在大学时期应该明确自己的发展方向,如果你在大一就确定你以后想成为Java工程师,那就不要花太多的时间去学习其他的技术语言,高数之类的,不如好好想着如何夯实Java基础。请转发本文支持一下。

2401_84048149的博客 1028

面试官:volatile关键字用过吧?说一下作用和实现吧

极简计算机发展史 我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区。而刚开始用户磁盘和CPU进行交互,CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,但是随着CPU的发展,内存的读写速度也远远跟不上CPU的读写速度,因此,为了解决这一纠纷,CPU厂商在每颗CPU上加入了高速缓存,用来缓解这种症状,因此,现在CPU同内存交互就变成了下面的样子。 单核CPU的性能不可能无限制的增长,要想很多的提升新能,需要多个处理器协同工作。 基于高速缓存的存储交互很好的解决了处理.

小识的博客 5989

指令重排序

之前面试的时候面试官问了这么一个问题:catch return finally还会执行吗。当时想的就是:啊!还可以这么写,从来没想过这种问题,但是想到finally一开始学的时候就说是一定是会执行的。基于这个,还是心虚的回答了:会执行,应该是先执行finally,再return。当时不知道是什么原理,后来想起来,决定整理个这个问题,做个记录。首先我们来用一段代码模拟下这个场景代码执行后结果如下:结果确实如我们想的那样,finally确实执行了,return也成功返回了,这是什么原因呢?

qq_31970227的博客 1055

面试官问:什么是Java内存模型?

本文介绍了Java内存模型(JMM)的核心概念及其在多线程编程中的重要性。JMM通过定义主内存与工作内存的交互规则,确保多线程环境下数据的可见性、原子性和有序性。文章详细解析了JMM的8种原子操作,并阐述了synchronized和volatile关键字如何实现内存一致性。最后区分了JMM与JVM内存结构的不同,强调JMM是规范而非具体实现。掌握JMM有助于理解Java并发机制,提升线程安全编程能力。

在技术的广袤天地里,本博客如精准罗盘。剖析前沿科技,深掘代码奥秘,以精炼笔触,带您穿越复杂技术迷宫,速达知识彼岸。 2916

NET多线程同步方法详解

.NET多线程同步方法详解(一):自由锁(InterLocked) 本文主要描述在C#中线程同步的方法。线程的基本概念网上资料也很多就不再赘述了。直接接入主题,在多线程开发的应用中,线程同步是不可避免的。在.Net框架中,实现线程同步主要通过以下的几种方式来实现,在MSDN的线程指南中已经讲了几种,本文结合作者实际中用到的方式一起说明一下。 1. 维护自由锁(InterLocked)实现同步 2. 监视器(Monitor)和互斥锁(lock) 3. 读写锁(ReadWriteLock) 4. 系统内核对象 1) 互斥(Mutex), 信号量(Semaphore), 事件(AutoResetEvent/ManualResetEvent)2) 线程池 除了以上的这些对象之外实现线程同步的还可以使用Thread.Join方法。这种方法比较简单,当你在第一个线程运行时想等待第二个线程执行结果,那么你可以让第二个线程Join进来就可以了。

startupinfo为什么需要初始化_面试官为什么总是问happens-before规则,看完这篇文章你就懂了...

本篇文章从happens-before定义、用途以及具体规则三个方面对happens-before进行解读,并通过源码案例深入了解为什么需要happens-before规则和什么是指令重排序。什么是happens-beforehappens-before: A happens-before B就是A先行发生于B(这种说法不是很准确),定义为hb(A, B)。在Java内存模型中,happens-b...

weixin_39898150的博客 82

Python+gensim【中文LDA】简洁模型

文章目录完整代码过程详解words_lsdictionarydictionary.doc2bow函数corpuslda 完整代码 from gensim import corpora, models import jieba.posseg as jp # 待分析文本集 text1 = '美国教练坦言,没输给中国女排,是输给了郎平' text2 = '中国女排世界排名第一?真实水平如何,听听巴西和美...

ArYe 2万+

Java内存模型:happens-before原则、指令重排序、内存栅栏、可见性与原子性深度解析

本场面试通过四轮提问,涵盖了Java内存模型的多个关键点。马架构凭借其十年的Java项目研发经验和架构设计经验,逐一解答了面试官提出的问题,并展现了深厚的理论知识和丰富的实战经验。

南客先生的博客 579

告别虚拟机!在Windows上用WSL+VSCode搞定ArduPilot开发环境(保姆级避坑指南)

本文提供了一份详细的保姆级指南,介绍如何在Windows上使用WSL和VSCode搭建高效的ArduPilot开发环境,告别虚拟机性能损耗。从WSL安装、VSCode配置到ArduPilot固件编译和调试技巧,涵盖开发全流程中的关键步骤和常见问题解决方案,帮助无人机开发者快速构建专业级开发环境。

weixin_42543046的博客 460

41-dify案例分享-基于database插件实现Text2sql的数据库查询图表工作流

本文介绍基于 Text2SQL 的 Dify 工作流实现过程。先阐述 Text2SQL 概念、步骤及应用领域。接着说明工作流制作,包括用 chatflow 加开场白、获取系统时间,配置 LLM 生成 SQL,用 SQL Execute 工具查询,经 LLM 汇总结果,代码生成 ECharts 数据,条件分支判断图表类型输出。最后介绍验证测试、提供体验地址,还提及借助工具生成建表和初始化数据 SQL 语句。

个人技术 1万+

【面试题】你知道指令重排以及happens-before原则是什么吗?

happens-before原则,一定程度上避免指令重排 编译器、指令器可能对代码重排序、乱排、要守一定的规则,happens-before原则。只要符合happens-before的原则,那么就不能胡乱重排序,如果不符合这个规则,那么就可以自己排序。 happens-before八大原则 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock

ksws01的博客 692

Demo2Reward:用人类演示动态优化VLM奖励模型提示

多模态大模型(VLM)的奖励建模正面临核心瓶颈——静态提示导致语义鸿沟与泛化失效。其本质是将人类判断标准固化为不可更新的文本模板,使奖励信号脱离真实任务意图。Demo2Reward提出范式级改进:以结构化人类演示为元信号,驱动提示权重连续优化,实现从‘结果打分’到‘过程理解’的跃迁。该方法不修改VLM主干,却显著提升RLHF收敛效率与OOD鲁棒性,在医疗、工业质检等场景验证了跨任务迁移与热修复能力。它重新定义了提示工程的技术价值:不是前端微调,而是奖励系统的认知校准基础设施。

weixin_34302798的博客 406

JAVA基础面试题:Java中的内存访问重排序(Memory Reordering)与happens-before关系的底层实现及其对多线程程序行为的影响

内存访问重排序本次面试围绕Java内存模型、多线程同步机制和锁优化技术展开了深入讨论。通过happens-before关系、volatile关键字、CAS操作等技术,Java能够在保证性能的同时,确保多线程程序的正确性。

qq_61302385的博客 966

ESP32锂电池电量检测实战:从引脚选择到低功耗优化(附完整电路图)

本文详细介绍了ESP32锂电池电量检测的实战方案,从ADC引脚选择到低功耗优化,提供完整的电路图和代码实现。通过MOS管智能开关和深度睡眠技术,显著降低静态功耗至0.5μA,提升设备续航能力。适合物联网开发者解决电量检测中的常见问题。

weixin_28731223的博客 459

dify源码本地部署,dify k8s部署

Dify.AI 是一款简单易用且开源的 LLMOps 平台,帮助开发者更简单、更快速地创建 AI 应用。它的核心理念是通过可声明式的 YAML 文件定义 AI 应用的各个方面,包括 Prompt、上下文和插件等。Dify 提供了可视化的 Prompt 编排、运营、数据集管理等功能。这些功能使得开发者能够在数天内完成 AI 应用的开发,或将 LLM 快速集成到现有应用中,并进行持续运营和改进。

shykevin的博客 1014

JMM内存模型-包括(指令重排序,volatile完整语义,happens-before原则等,带图)

记录下自己对JMM的总结,包括重排序;特殊关键字volatile、final的语义;happens-before原则等

Boan 474

从压电传感器到示波器:手把手教你用CA3140搭建电荷放大器(含PCB布局与调零技巧)

本文详细介绍了如何使用CA3140运算放大器搭建专业级电荷放大器,从压电传感器信号特性分析到PCB布局与调零技巧,全程实战演示。重点解析了CA3140的低通滤波器设计、反馈网络参数计算及抗干扰增强方案,帮助电子爱好者高效实现微弱电荷信号的稳定转换与测量。

weixin_31355483的博客 596

JAVA基础面试题:Java中的内存屏障(Memory Barrier)与指令重排序对多线程程序行为的影响

内存屏障内存屏障面试官:Victor,你的回答非常全面,体现了扎实的技术功底和系统性思维。Victor很高兴能有机会深入讨论这些技术问题。

qq_61302385的博客 964

内容创作全流程自动化:OpenClaw+大模型搞定选题+写稿+多平台发布

本文介绍了一套基于OpenClaw的全自动化内容创作流水线方案,帮助垂直领域创作者摆脱重复劳动。该方案将内容生产拆解为选题生成、内容撰写、格式适配、多平台发布四个模块,通过工作流引擎串联执行。系统能自动采集热榜生成选题、调用大模型撰写结构化内容、适配不同平台格式要求,并通过API+浏览器自动化实现多平台发布,全程只需人工最终审核。作者分享了各模块的实现细节,包括热榜采集、内容结构化生成、Markdown转换和发布策略,并提供了工作流编排方法。这套方案能为创作者每天节省约3小时,同时保证内容质量和发布效率。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 49

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Java面试题:Java内存模型深度剖析,Java内存模型中的重排序(Reordering)现象,Java内存模型中的happens-before关系

例如,在使用synchronized块时,一个线程解锁一个对象(即退出synchronized块)的操作happens-before后续任何线程对该对象的加锁操作(即进入synchronized块),从而保证了操作的顺序性。在Java的并发编程中,内存模型扮演着至关重要的角色。通过面试题的形式,我们可以更深入地评估应聘者对Java内存模型的理解程度以及其在实际编程中的应用能力。当操作A happens-before 操作B时,操作A的结果对操作B是可见的,并且操作B不会在操作A之前发生。

杰哥在此的专栏 546

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: ConcurrentHashMap里面也有死循环
下一篇: 面试官80%会问的分布式事务中的“最大努力通知”事务
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值