深入理解 Happens-Before 原则

Happens-Before原则深入解读 Happens-Before(先行发生)原则是对Java内存模型(JMM)中所规定的可见性的更高级的语言层面的描述。用这个原则解决并发环境下两个操作之间的可见性问题,而不需要陷入Java内存模型苦涩难懂的定义中。... 阅读详情

今天就让我们来深入聊聊关于 happens-before 的那些事儿!

什么是 happens-before?

happens-before 指的是 Java 内存模型中两项操作的顺序关系。例如说操作 A 先于操作 B,也就是说操作 A 发生在操作 B 之前,操作 A 产生的影响能够被操作 B 观察到。这里的「影响」包括:内存中共享变量的值、发送了消息、调用了方法等。

举个很简单的例子:下面代码里 i=1 在线程 A 中执行,而 j=i 在线程 B 中执行。因为 i=1 操作先于 j=i 执行,那么 i=1 操作的结果就应该能够被线程 B 观察到。

// 在线程 A 中执行
i = 1;
// 在线程 B 中执行
j = i;

Java 内存模型下一共有 8 条 happens-before 规则,如果线程间的操作无法从如下几个规则推导出来,那么它们的操作就没有顺序性保障,虚拟机或者操作系统就能随意地进行重排序,从而可能会发生并发安全问题。

  • 程序次序规则(Program Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。
  • 管程锁定规则(Monitor Lock Rule):一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。这里必须强调的是同一个锁,而 “后面” 是指时间上的先后顺序。
  • volatile 变量规则(Volatile Variable Rule):对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作,这里的 “后面” 同样是指时间上的先后顺序。
  • 线程启动规则(Thread Start Rule):Thread 对象的 start () 方法先行发生于此线程的每一个动作。
  • 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过 Thread.join () 方法结束、Thread.isAlive () 的返回值等手段检测到线程已经终止执行。
  • 线程中断规则(Thread Interruption Rule):对线程 interrupt () 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 Thread.interrupted () 方法检测到是否有中断发生。
  • 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize () 方法的开始。
  • 传递性(Transitivity):如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那就可以得出操作 A 先行发生于操作 C 的结论。

极简实践案例

Java 语言无须任何同步手段保障,就能成立的先行发生规则,就只有上面这些了。下面举个例子来说明如何用这些规则去判断操作是否具备顺序性,是否是线程安全的。

private int value = 0;

public void setValue(int value){
    this.value = value;
}

public int getValue(){
    return value;
}

上面的代码是一组很普通的 getter/setter 方法。假设线程 A 和 B,线程 A 先(时间上的先后)调用了 setValue(1),之后线程 B 调用了同一个对象的 getValue(),那么线程 B 收到的返回值是什么?

我们依次分析一下先行发生原则中的各项规则:

  • 首先,由于两个方法分别由线程 A 和线程 B 调用,不在一个线程中,所以程序次序规则在这里不适用。
  • 接着,由于没有同步块,自然就不会发生 lock 和 unlock 操作,所以管程锁定规则不适用。
  • 继续,由于 value 变量没有被 volatile 关键字修饰,所以 volatile 变量规则不适用。
  • 继续,后面的线程启动、终止、中断规则和对象终结规则也和这里完全没有关系。
  • 最后,因为没有一个适用的先行发生规则,所以最后一条传递性也无从谈起。

因此,即使我们知道线程 A 在操作时间上先于线程 B,但我们还是无法确定线程 B getValue() 方法的返回结果。换句话说,这里面的操作不是线程安全的。

那怎么修复这个问题呢?

我们至少有两种比较简单的方案可以选择:

  • 第一种,要么把getter/setter​ 方法都定义为synchronized 方法,这样就可以套用管程锁定规则。
  • 第二种,要么把value​ 定义为volatile​ 变量,由于setter​ 方法对value​ 的修改不依赖value​ 的原值,满足volatile​ 关键字使用场景,这样就可以套用volatile 变量规则来实现先行发生关系。

通过上面这个案例,我们知道:一个操作时间上线发生,不代表这个操作会「先行发生」。 那如果一个操作「先行发生」,是否就能推导出这个操作必定是时间上先发生呢?其实并不能,因为有可能发生指令重排序。

// 如下操作在同一个线程中执行
int j = 1;
int j = 2;

上述代码在同一线程中执行,根据程序执行次序规则,int i = 1; 的操作先行发生于 int j = 2;,但 int j =2 的代码有可能被处理器先执行,因为它们不相互依赖,不影响先行发生原则的正确性。

上述这两个案例综合起来证明了一个结论:时间先后顺序与先行发生原则之间基本没有太大的关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。

总结

happens-before 原则一共有 8 条原则,它是对 Java 内存模型规则的简化,可以帮助编程人员提高编程效率。

时间先后顺序与先行发生原则之间基本没有太大的关系,我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。

深入理解 happens-before 原则

并发中的happens-before原则 跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理。 作者简介:笔名seaboat,擅长工程算法、人工智能算法、自然语言处理、计算机视觉、架构、分布式、高并发、大数据和搜索引擎等方面的技术,大多数编程语言都会使用,但更擅长Java、Python和C++。平时喜欢看书写作、运动、画画。崇尚技术自由,崇尚思想自由。出版书籍:《Tomcat内核设计剖析》、《图解数据结构与算法》、《图解Java并发原理》、《人工智能原理科普》。 happens-before原则 我们一般将Jav. 阅读详情

相关推荐

Java并发中Happens-before八大原则,附代码例子讲解

跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理。 作者简介:笔名seaboat,擅长工程算法、人工智能算法、自然语言处理、架构、分布式、高并发、大数据和搜索引擎等方面的技术,大多数编程语言都会使用,但更擅长Java、Python和C++。平时喜欢看书写作、运动、画画。崇尚开源,崇尚技术自由,更崇尚思想自由。出版书籍:《Tomcat内核设计剖析》、《图解数据结构与算法》、《图解Java并发原理》、《人工智能原理科普》。 单线程原则 单线程原则是最简单的规则,就是说在单个.

seaboat——a free boat on the sea.(公众号:远洋号) 806

什么是happens-before原则?

前言 本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见100个问题搞定Java虚拟机 正文 happens-before 关系是用来描述两个操作的内存可见性的。如果操作 X happens-before 操作 Y,那么 X 的结果对于 Y 可见。 Java 内存模型底层是通过内存屏障(memory barrier)来禁止重排序的。 线程内的 happens-before 在同一个线程中,字节码的先

Shockang的博客 1万+

使用 Java + PaddleOCR 实现表格识别,支持导出 HTML 和 Excel(附源码)

使用Java+PaddleOCR快速搭建表格识别系统,无需处理底层模型细节。系统支持表格结构识别和内容识别,可将图片转换为可编辑表格格式。文章提供了完整的实现流程,包括Maven依赖引入、模型下载(包含检测、识别、分类和表格结构模型)及参数配置说明。SmartJavaAI支持多种高性能模型选择,适用于不同应用场景,帮助开发者快速实现表格识别功能。

deng775747758的博客 1474

Happens-Before原则的通俗理解

指令重排&happens-before 原则 & 内存屏障:https://www.cnblogs.com/ITPower/p/13580691.html Java内存模型:https://www.cnblogs.com/yuanfy008/p/9252555.html

pscool的博客 2667

光学系统设计:波段、光源、光纤与探测器匹配指南

在光学测量和光纤通信系统中,波段、光源、光纤和探测器的匹配关系是系统设计的核心基础。波段指电磁波谱中的特定波长范围,光源负责产生特定波长的光信号,光纤作为传输介质需要与波长匹配以降低损耗,探测器则负责将光信号转换为电信号。理解各组件的光谱特性对于系统性能至关重要,例如在850nm短波长多模光纤系统中,VCSEL激光器与PIN光电二极管的组合能提供最佳性价比。典型应用场景包括生物医学成像、光纤通信和气体传感,其中光谱重叠优化、模场直径匹配和偏振特性协调是关键技术要点。通过实测数据对比不同组合的传输性能,可以为

weixin_33812433的博客 338

SBP依赖计算中的蝴蝶效应:一个Prefab修改如何触发全量重建?

本文深入探讨了Unity中SBP(Scriptable Build Pipeline)依赖计算的蝴蝶效应,解析了Prefab修改如何触发全量重建的机制。通过分析Asset与Object的层级关系、依赖计算的三种维度及CacheEntry系统,揭示了全量重建的根本原因,并提供了依赖可视化工具开发、优化策略与最佳实践,帮助开发者提升构建效率。

weixin_29015051的博客 444

Happens-Before原则(先行发生原则)

Happens-Before 从jdk5开始,java使用新的JSR-133内存模型,基于Happens-Before的概念来阐述操作之间的内存可见性。 Happens-Before定义 如果一个操作Happens-Before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。 两个操作之间存在Happens-Before关系,并不意味着一...

FengDuidui的博客 2331

LabVIEW内存泄漏诊断与优化:从报错到高效运行

本文深入探讨LabVIEW内存泄漏的诊断与优化方法,从理解内存泄漏本质到使用专业工具链进行问题定位,再到高频场景的解决方案和高级优化策略。通过实战案例展示如何解决工业数据采集系统中的内存不足问题,帮助开发者提升LabVIEW应用的稳定性和效率。

moon9的博客 197

【字节一面】解释下什么是 happens-before?

happens-before(先行发生)是Java中一个重要的多线程概念,用于描述不同线程之间操作的执行顺序。它是Java内存模型(Java Memory Model)

激流丶的博客 2687

把 SAP Fiori Launchpad Designer 的 Catalog 与 Group 传输讲透:从 CONF、CUST 到 Transport Request 的完整理解

SAP Fiori项目中,catalog和group的传输问题往往源于对FLPD层次模型的理解不足。FLPD包含configuration(系统级)和customizing(客户端级)两个层面,分别对应workbench request和customizing request传输方式。常见误区包括:混淆/UI2/FLPD_CONF和/UI2/FLPD_CUST入口、未提前创建传输请求、不了解层次覆盖机制(customizing会覆盖configuration)。当客户端修改configuration层的对象

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 101

EUV光源技术路线深度解析

EUV光刻技术的关键瓶颈在于13.5纳米光源的稳定高效获取。主流技术路线从DPP放电等离子体发展为LPP激光等离子体方案,后者通过激光轰击锡液滴实现更高功率输出。新出现的LDP技术综合激光预热与放电优势,虽功率扩展性有限,但为光源技术提供了有益补充。目前LPP仍是唯一通过量产验证的方案,而LDP可能作为过渡技术应用于特定场景。EUV光源的发展实质是能量转化效率与系统可靠性的持续优化过程,各种技术路线的探索共同推动着这一核心工艺的进步。

硬核科技 556

深入理解 happens-before 原则

在前面的文章中(从 CPU 说起,深入理解 Java 内存模型!),我们深入了解了 Java 内存模型,知道了 Java 内存模型诞生的意义,以及其要解决的问题。最终我们知道:Java 内存模型就是定义了 8 个基本操作以及 8 个规则,只要遵守这些规则的并发操作,那么它们就是安全的。即使强如树哥的人,看了这 16 条规则也很头疼。它们太过于繁琐了,非常不利于我们日常代码...

陈树义 372

Unity插件NodeCanvas之行为树的详细教程

NodeCanvas 是一个用于 Unity 的可视化脚本工具,主要用于游戏开发中的行为树、状态机和流程图的设计。它通过节点编辑器的方式,让开发者可以更直观地创建和管理游戏逻辑,减少了传统编码的复杂性。今天我们就来详细介绍一下NodeCanvas插件。

TxNet 1958

深入理解 Java 中的 Happens-Before 原则

在上述代码中,线程 B 中的操作(example.value = 1)happens-before 线程 A 中调用 join() 方法后后续的操作(System.out.println(example.value))。即一个线程中的操作按照代码的顺序执行,前面的操作 happens-before 后面的操作。线程 A 调用线程 B 的 join() 方法并成功返回时,线程 B 中的所有操作 happens-before 线程 A 中后续的操作。// 操作 A happens-before 操作 B。

m0_74337424的博客 932

泛微OA集成阿里云短信(E9保姆教程)

泛微OA集成阿里云短信(E9保姆教程)

技术分享 515

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

深入理解Happens-Before原则:以实例解析并发编程的基石

Happens-Before原则是Java内存模型(JMM)中定义的一种偏序关系,用于确定两个操作之间的相对顺序。如果操作A happens-before 操作B,那么操作A的结果对操作B是可见的,且操作A的执行顺序在操作B之前。这就意味着,当线程B读取到线程A写入的值时,可以确保线程A的写入操作已经发生。

小威的博客 1万+

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Java面试必修课】深入理解happens-before原则,告别线程安全问题!

在多线程编程中,和问题就像潜伏的幽灵,稍不留神就会导致程序崩溃。Java通过JMM(Java Memory Model)提供了一套内存可见性规则,而其中的正是解决这类问题的核心钥匙!本文将用实际代码案例,带你彻底搞懂这个让无数开发者头疼的“玄学”规则!

lisort的博客 1837

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

《深入理解Java内存模型从Volatile到Happens-Before原则的探索》

Java内存模型(Java Memory Model, JMM)是Java虚拟机规范中定义的一种抽象概念,用于屏蔽各种硬件和操作系统的内存访问差异,以实现Java程序在各种平台下都能达到一致的内存访问效果。JMM的核心目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。它围绕并发编程中三个核心特性展开:原子性、可见性和有序性。

xxh2jd的博客 658

happens-before原则

一、happens-before 原则

放羊的大飞 464

我真不想学 happens - before 了!

这个我想是大家学习 Java并发编程中非常容易忽略的一个点,为什么,因为太抽象了。我刚开始学习的时候遇到 happens-before 的时候也是不明觉厉,"哪来的这么一个破玩意...

方志朋的博客 120

JAVA基础-深入理解Java内存模型(上)-- 重排序与先行发生原则(happens-before)

介绍Java内存模型-重排序和先行发生原则(happens-before)

致橡树 1223

一个例子带你记住happens-before

一个例子带你记住happens-before package com.mtons.mblog.leetcode; /** * 一个例子带你记住happens-before * 规则一:程序的顺序性规则 * 一个线程中,按照程序的顺序,前面的操作happens-before后续的任何操作。 * 对于这一点,可能会有疑问。顺序性是指,我们可以按照顺序推演程序的执行结果,但是编译 * 器未必一定会按照这个顺序编译,但是编译器保证结果一定==顺序推演的结果。 * * 2. 规则二:volatile

开心就好 6453

Java内存模型中的happens-before原则

先行发生原则(Happens-Before)是判断数据是否存在竞争、线程是否安全的主要依据。 先行发生是Java内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,那么操作A产生的影响能够被操作B观察到。 口诀:如果两个操作之间具有happen-before关系,那么前一个操作的结果就会对后面的一个操作可见。是Java内存模型中定义的两个操作之间的偏序关系。 常见的happ...

Carlos的博客 303

【happen-before原则】

happen-before原则JMM内存模型JMM定义指令重排序什么是happen-before JMM内存模型 java内存模型是共享内存的并发模型,线程之间主要通过读-写共享变量来完成隐式通信。java中的共享变量是存储在内存中的,多个线程由其工作内存,其工作方式是将共享内存中的变量拿出来放在工作内存,操作完成后,再将最新的变量放回共享变量,这时其他的线程就可以获取到最新的共享变量。 JMM定义 Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各个硬件

qq_36348243的博客 466

JAVA多线程的初级认识3-happens-before原则

针对java的happens-before有很多原则,个人总结起来如下,做个笔记别忘记: 程序有序原则 如何理解呢?个人理解就是无论程序如何优化,包括指令集重排序,hotspot的c1,c2运行时候优化等,都不会更改结果。即无论如何优化,结果不会变。 volatile原则 对volatile修饰的变量写的操作一定发生在读的操作之前。即我们读取出来的volatile变量一定是最新的。个人感觉...

comil4444的专栏 171
上一篇: 面经:两年半经验,面10个公司,经28轮面试,拿9个offer,涨麻了!
下一篇: 用了那么久的 Java For 循环,你知道哪种方式效率最高吗?
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值