Java核心技术面试精讲
杨晓峰
前Oracle首席工程师
立即订阅
43250 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 以面试题为切入点,有效提升你的Java内功
免费
模块一 Java基础 (14讲)
第1讲 | 谈谈你对Java平台的理解?
第2讲 | Exception和Error有什么区别?
第3讲 | 谈谈final、finally、 finalize有什么不同?
第4讲 | 强引用、软引用、弱引用、幻象引用有什么区别?
第5讲 | String、StringBuffer、StringBuilder有什么区别?
第6讲 | 动态代理是基于什么原理?
第7讲 | int和Integer有什么区别?
第8讲 | 对比Vector、ArrayList、LinkedList有何区别?
第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?
第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?
第12讲 | Java有几种文件拷贝方式?哪一种最高效?
第13讲 | 谈谈接口和抽象类有什么区别?
第14讲 | 谈谈你知道的设计模式?
模块二 Java进阶 (16讲)
第15讲 | synchronized和ReentrantLock有什么区别呢?
第16讲 | synchronized底层如何实现?什么是锁的升级、降级?
第17讲 | 一个线程两次调用start()方法会出现什么情况?
第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?
第19讲 | Java并发包提供了哪些并发工具类?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?
第22讲 | AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
第23讲 | 请介绍类加载过程,什么是双亲委派模型?
第24讲 | 有哪些方法可以在运行时动态生成一个Java类?
第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?
第26讲 | 如何监控和诊断JVM堆内和堆外内存使用?
第27讲 | Java常见的垃圾收集器有哪些?
第28讲 | 谈谈你的GC调优思路?
第29讲 | Java内存模型中的happen-before是什么?
第30讲 | Java程序运行在Docker等容器环境有哪些新问题?
模块三 Java安全基础 (2讲)
第31讲 | 你了解Java应用开发中的注入攻击吗?
第32讲 | 如何写出安全的Java代码?
模块四 Java性能基础 (3讲)
第33讲 | 后台服务出现明显“变慢”,谈谈你的诊断思路?
第34讲 | 有人说“Lambda能让Java程序慢30倍”,你怎么看?
第35讲 | JVM优化Java代码时都做了什么?
模块5 Java应用开发扩展 (4讲)
第36讲 | 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
第37讲 | 谈谈Spring Bean的生命周期和作用域?
第38讲 | 对比Java标准NIO类库,你知道Netty是如何实现更高性能的吗?
第39讲 | 谈谈常用的分布式ID的设计方案?Snowflake是否受冬令时切换影响?
周末福利 (2讲)
周末福利 | 谈谈我对Java学习和面试的看法
周末福利 | 一份Java工程师必读书单
结束语 (1讲)
结束语 | 技术没有终点
Java核心技术面试精讲
登录|注册

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

杨晓峰 2018-07-12
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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java核心技术面试精讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(25)

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

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

    2018-08-10
    2
    9
  • 公号-代码荣耀
    可从四个维度去理解JMM

    1 从JVM运行时视角来看,JVM内存可分为JVM栈、本地方法栈、PC计数器、方法区、堆;其中前三区是线程所私有的,后两者则是所有线程共有的

    2 从JVM内存功能视角来看,JVM可分为堆内存、非堆内存与其他。其中堆内存对应于上述的堆区;非堆内存对应于上述的JVM栈、本地方法栈、PC计数器、方法区;其他则对应于直接内存

    3 从线程运行视角来看,JVM可分为主内存与线程工作内存。Java内存模型规定了所有的变量都存储在主内存中;每个线程的工作内存保存了被该线程使用到的变量,这些变量是主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量

    4 从垃圾回收视角来看,JVM中的堆区=新生代+老年代。新生代主要用于存放新创建的对象与存活时长小的对象,新生代=E+S1+S2;老年代则用于存放存活时间长的对象
    2018-07-12
    66
  • - -
    个人觉得happens-before原则最难理解的就是和时间次序上的关系,就比如volatile变量的写操作happens-before其后的读操作,之前很难理解“其后”的含义,一直认为既然是先发生的操作,结果肯定对后续的操作可见啊,以至于认为这个原则是不是多余的。结合了jmm内存模型来看的话就很好理解了,一个操作完成,其结果只是在线程内可见的,在结果写回主存并被其他线程读取前,即使其他线程操作靠后,也无法看见其操作结果。所以才会有volatile、锁等一系列可见性原则的约束
    2018-07-19
    21
  • 三木子
    刚看完文章,在看了下《深入JAVA虚拟机》的java内存模型章节,又加深点印象。这本书真不能像小说一样读!
    2018-07-12
    10
  • 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
    8
  • Ab
    jmm可以从两个方面理解,第一个方面是jmm规范了一个抽象的内存结构,jmm运行时内存进行简化抽象得到了主内存和本地内存两块内存区域,在线程运行时,从主内存中加载数据到本地内存,在本地内存中完成计算后,在刷新到主内存。第二个方面是jmm可以理解我一组保证数据内存可见性和程序正确性的规则,由于这种内存模型存在很明显的数据一致性问题,再加上编译器的指令重排序和cpu乱序执行优化,使问题更加复杂了,而jmm就是通过类似内存屏障等手段保证了内存可见性问题以及在多线程环境下乱序优化和指令重排序带来的线程安全性问题。从使用者的角度理解,jmm实际上平衡了java程序员对简单性的渴望和jvm工程师cpu工程师对性能的追求的平衡,面向底层时,jmm在保证正确性的同时最大限度的放宽了对指令重排序和乱序执行优化的限制,面向上层jmm通过内存屏障实现了volatile和synchronized等内存语意,使程序员可以简单方便的应用这些特性来保证程序的程序的正确性。
    2018-07-18
    6
  • 风轨
    先看了两遍,始终处于懵逼的状态,后来去把《深入理解java虚拟机》相关部分仔细阅读一下,再回来看终于看懂了!
    2018-08-14
    3
  • 蠢蠢欲动的腹肌
    在网上看了下让双检锁生效的方法,除了用volatile修饰变量外,还有其他两种方式
    1、用final 修饰变量
    2、用本地线程的方式修复,即在创建对象时存取本地线程(final的),在get的时候再从本地取

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

    2018-08-14
    2
  • clz1341521
    1,验证出 “可见性问题”,这个很容复现
    2,验证出“cpu指令优化重排”导致的如 双检锁 中对象未初始化完毕,即被使用问题。这个对象构造要慢,才容易复现
    2018-08-10
    1
  • 西兹兹
    老师讲了JSR133里的1个规则, volatile
    2019-11-11
  • ddddd🐳
    关于volatile的这句描述-“对于 volatile 变量,对它的写操作,保证 happen-before 在随后对该变量的读取操作。”,我始终不理解,写了很多demo测试,也没弄清楚,老师能给个简单的demo code吗?;
    2019-10-31
  • 随心而至
    配合深入理解Java虚拟机第12章食用,效果更佳
    2019-09-20
  • yzh
    老是,具体有什么工具可以验证JMM的所有执行可能
    2019-08-15
  • 浪尖
    读操作插入的屏障,不都是在volatile读之后吗?
    2019-07-03
    1
  • 谢涛
    茅塞顿开了,赞
    2019-06-06
  • 冰激凌的眼泪
    单线程内的happens before和优化后的有序性不冲突吗?这里的happens before是指的高级语言语句级还是cpu指令级呢?
    2019-03-03
    1
  • Pan
    杨老师,请教个问题,前面说到的volatile变量是在写操作之后,编译器插入一个写屏障,为什么不是在写操作之前,和我理解有点出入,望赐教。
    2019-02-20
  • Seven4X
    之前的问题找到解决方法了,就是使用concurrenthashmap的computeIfAbsent方法,这个方法是加锁的.
    文中提到的volatile的例子,我试了一下并没有复现出来,根据《深入理解JVM》这本书关于volatile的例子复现出来了,了解了volatile并不能保证字段线程安全
    然后Brian Goetz的《java theory and practice:managing volatility》仔细拜读了一下,还是没有找到能够百分百复现出来的办法
    请问有什么方法能够稳定复现出来文中的例子吗?
    2018-08-02
  • philip
    老师volatile变量的happens_befor中ues之前必须load。但是load了不中间不对变量进行操作,过一会再操作,但是其他线程已经操作了volitile变量。此时被加载到工作区的volatile变量再执行assgin操作时应该不会重新加载主存数据了对不?
    2018-07-20
  • 饭磊
    老师我想问下 为什么jvm里面有时候说的是绑定,有时候说是分派,请问这俩什么区别呀
    2018-07-19
收起评论
25
返回
顶部