Java 核心技术面试精讲
杨晓峰
前 Oracle 首席工程师
125942 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 44 讲
Java 核心技术面试精讲
15
15
1.0x
00:00/00:00
登录|注册

第29讲 | Java内存模型中的happen-before是什么?

如何验证所有符合JMM执行可能?有什么工具可以辅助?
volatile变量的可见性发生了增强,能够守护其上下文的作用
JMM内部的实现通常依赖于内存屏障,通过禁止某些重排序的方式提供内存可见性保证
JMM试图解决多线程程序正确性和可移植性的问题
Java是最早尝试提供内存模型的语言,早期类似C、C++等语言并不存在内存模型的概念
建议结合例子梳理为什么需要JMM以及JMM是如何解决可见性等问题的
学习JMM需要明确目的,克制住技术的诱惑,克制住对“秘籍”的诱惑
Java内存模型基本概念是面试的热点
这些关系具有传递性
具体表现形式包括线程内操作的顺序保证、volatile变量的读写操作顺序保证、锁的解锁操作顺序保证等
Happen-before关系是Java内存模型中保证多线程操作可见性的机制
一课一练
知识扩展
考点分析
典型回答
Java内存模型中的happen-before关系

该思维导图由 AI 生成,仅供参考

Java 语言在设计之初就引入了线程的概念,以充分利用现代处理器的计算能力,这既带来了强大、灵活的多线程机制,也带来了线程安全等令人混淆的问题,而 Java 内存模型(Java Memory Model,JMM)为我们提供了一个在纷乱之中达成一致的指导准则。
今天我要问你的问题是,Java 内存模型中的 happen-before 是什么?

典型回答

Happen-before 关系,是 Java 内存模型中保证多线程操作可见性的机制,也是对早期语言规范中含糊的可见性概念的一个精确定义。
它的具体表现形式,包括但远不止是我们直觉中的 synchronized、volatile、lock 操作顺序等方面,例如:
线程内执行的每个操作,都保证 happen-before 后面的操作,这就保证了基本的程序顺序规则,这是开发者在书写程序时的基本约定。
对于 volatile 变量,对它的写操作,保证 happen-before 在随后对该变量的读取操作。
对于一个锁的解锁操作,保证 happen-before 加锁操作。
对象构建完成,保证 happen-before 于 finalizer 的开始动作。
甚至是类似线程内部操作的完成,保证 happen-before 其他 Thread.join() 的线程等。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java内存模型中的happen-before关系是多线程操作可见性的重要机制,保证了线程内操作的顺序性,包括程序顺序规则、volatile变量的写操作和随后的读取操作、锁的解锁操作和加锁操作、对象构建完成和finalizer的开始动作等。这些关系具有传递性,满足a happen-before b和b happen-before c,则a happen-before c也成立。文章建议在学习Java内存模型时,明确目的,避免过度关注CPU体系结构等细节,同时不要依赖于特定的编程方式,而是遵循语言规范进行编程,以确保应用行为可靠、可预测。对于读者,建议结合实例了解JMM解决可见性等问题的方式,例如volatile在具体用例中的效果。文章内容深入浅出,既涵盖了面试热点,又提供了实际编程建议,适合读者快速了解Java内存模型中的happen-before概念。文章还介绍了JMM的背景和解决可见性问题的方式,以及volatile在多线程场景中的典型用例。通过对JMM执行的验证和辅助工具的讨论,读者可以更好地理解和应用JMM的规范。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 核心技术面试精讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(35)

  • 最新
  • 精选
  • clz1341521
    置顶
    杨老师,请教一个问题,望答复。 volatile boolean和atomicboolean 一样是原子性的吗?

    作者回复: volatile只保证可见性

    2018-08-10
    6
    34
  • 蠢蠢欲动的腹肌
    在网上看了下让双检锁生效的方法,除了用volatile修饰变量外,还有其他两种方式 1、用final 修饰变量 2、用本地线程的方式修复,即在创建对象时存取本地线程(final的),在get的时候再从本地取

    作者回复: 对,这里有各种tricky的技巧

    2018-08-14
    9
  • 3W1H
    老师的例子里面的thread a,b的逻辑是在一个方法里面吗?

    作者回复: 和几个方法没关系

    2018-07-12
    1
  • 刘杰
    您又说到了单例模式中的那个其他线程访问到未初始完成对象的问题,忍不住再问下,是否可以先用一个局部变量初始化对象,再把局部变量赋值给类成员?这样可以解决吗?

    作者回复: 不清楚,这个我没想到规范中哪一条可以保证; 建议看看比较权威的文章 https://shipilev.net/blog/2014/safe-public-construction/#_safe_initialization

    2018-07-13
  • - -
    个人觉得happens-before原则最难理解的就是和时间次序上的关系,就比如volatile变量的写操作happens-before其后的读操作,之前很难理解“其后”的含义,一直认为既然是先发生的操作,结果肯定对后续的操作可见啊,以至于认为这个原则是不是多余的。结合了jmm内存模型来看的话就很好理解了,一个操作完成,其结果只是在线程内可见的,在结果写回主存并被其他线程读取前,即使其他线程操作靠后,也无法看见其操作结果。所以才会有volatile、锁等一系列可见性原则的约束
    2018-07-19
    2
    63
  • Ab
    jmm可以从两个方面理解,一是抽象内存结构,jmm把内存结构抽象成主内存和线程本地内存两种,在计算时,从主内存中加载数据,在本地内存计算,然后在刷新到主内存,但这种模型有明显的一致性问题,二是jmm可以理解我一组保住内存可见性及成正确性的规范,因为这种模型存在明显的一致性问题,同时,由于java编译器指令重排序优化和cpu乱序执行优化的存在,使问题变得更加复杂,所以jmm基于内存屏障提供了类似sa if serial以及happens before的保障。从使用者的角度理解,jmm平衡了jvm工程师以及cpu工程师在性能上的需求和java程序员在简单性上的渴望,所以jmm在保证正确性的同时会最大限度的放宽对指令重排和乱序执行的限制。对于java程序员,jmm提供了如volatile和synchronized这样的顶层机制为程序员提供简单的编程模型。(参考老师的文章及java并发编程艺术 理解)
    2018-07-18
    23
  • 李二木
    刚看完文章,在看了下《深入JAVA虚拟机》的java内存模型章节,又加深点印象。这本书真不能像小说一样读!
    2018-07-12
    1
    14
  • 张三
    volitaile的意义在于禁用缓存
    2020-06-02
    9
  • Ab
    jmm可以从两个方面理解,第一个方面是jmm规范了一个抽象的内存结构,jmm运行时内存进行简化抽象得到了主内存和本地内存两块内存区域,在线程运行时,从主内存中加载数据到本地内存,在本地内存中完成计算后,在刷新到主内存。第二个方面是jmm可以理解我一组保证数据内存可见性和程序正确性的规则,由于这种内存模型存在很明显的数据一致性问题,再加上编译器的指令重排序和cpu乱序执行优化,使问题更加复杂了,而jmm就是通过类似内存屏障等手段保证了内存可见性问题以及在多线程环境下乱序优化和指令重排序带来的线程安全性问题。从使用者的角度理解,jmm实际上平衡了java程序员对简单性的渴望和jvm工程师cpu工程师对性能的追求的平衡,面向底层时,jmm在保证正确性的同时最大限度的放宽了对指令重排序和乱序执行优化的限制,面向上层jmm通过内存屏障实现了volatile和synchronized等内存语意,使程序员可以简单方便的应用这些特性来保证程序的程序的正确性。
    2018-07-18
    9
  • 风轨
    先看了两遍,始终处于懵逼的状态,后来去把《深入理解java虚拟机》相关部分仔细阅读一下,再回来看终于看懂了!
    2018-08-14
    7
收起评论
显示
设置
留言
35
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部