跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

java内存模型(Java Memory Model,JMM)是java虚拟机规范定义的,用来屏蔽掉java程序在各种不同的硬件和操作系统对内存的访问的差异,这样就可以实现java程序在各种不同的平台上都能达到内存访问的一致性。可以避免像c++等直接使用物理硬件和操作系统的内存模型在不同操作系统和硬件平台下表现不同,比如有些c/c++程序可能在windows平台运行正常,而在linux平台却运行有问题。

提纲

java线程之间的通信对程序员完全透明,内存可见性问题很容易困扰java程序员,本文试图揭开java内存模型神秘的面纱。本文大致分三部分:重排序与顺序一致性;三个同步原语(lock,volatile,final)的内存语义,重排序规则及在处理器中的实现;java内存模型的设计目标,及其与处理器内存模型和顺序一致性内存模型的关系。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

《深入理解 Java 内存模型》,该书在以前看过一遍,现在学的东西越多,感觉那块越重要,于是又再细看一遍,于是便有了下面的读书笔记总结。全书页数虽不多,内容讲得挺深的。细看的话,也是挺花时间的,看完收获绝对挺大的。也建议 Java 开发者都去看看。里面主要有 Java 内存模型的基础、重排序、顺序一致性、Volatile 关键字、锁、final。本文参考书中内容。如果有需要这份文档用来学习的朋友,请看文末获取方式!

基础

  • 并发编程模型的分类
  • Java内存模型的抽象
  • 重排序
  • 处理器重排序与内存屏障指令
  • happens-befere

并发编程模型的分类

在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。同步是指程序用于控制不同线程之间操作发生相对顺序的机制。在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。Java的并发采用的是共享内存模型, Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。如果编写多线程程序的Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

资料全部真实有效,不会缺页,不会只是大纲!!!由于文章篇幅有限,为了不耽误大家宝贵的时间,这份PDF小编已经为大家整理好了,需要免费领取的朋友麻烦帮忙转发、转发、转发这篇文章(一定要转发哦),然后私信小编【学习】或【进阶】免费获取。

重排序

  • 数据依赖性
  • as-if-serial语义
  • 程序顺序规则
  • 重排序对多线程的影响

数据依赖性

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。前面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。注意,这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

顺序一致性

  • 数据竞争与顺序一致性保证
  • 顺序一致性内存模型
  • 同步程序的顺序一致性效果
  • 未同步程序的执行特性

数据竞争与顺序一致性保证

当程序未正确同步时,就可能会存在数据竞争。java 内存模型规范对数据竞争的定义如下:

●在一个线程中写-一个变量,

●在另一个线程读同-一个变量,

●而且写和读没有通过同步来排序。

当代码中包含数据竞争时,程序的执行往往产生违反直觉的结果(前一章的示例正是如此)。如果一个多线程程序能正确同步,这个程序将是一个没有数据竞争的程序

JMM对正确同步的多线程程序的内存-致性做了 如下保证: .

●如果程序是 正确同步的。程序的执行将具有顺序一致性( sequentillyconsistent ) -即程序的执行结果与该程序在顺序-致性内存模型中的执行结果相同。马上我们将会看到,这对于程序员来说是一个极强的保证。 这里的同步是指广义上的同步,包括对常用同步原语( synchroniced , volatile和final )的正确使用。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

VOLATILE

  • volatile的特性
  • volatile写读建立的happens before关系
  • volatile写读的内存语义
  • volatile
  • 内存语义的实现
  • JSR-133为什么要增强volatile的内存语义

volatile的特性

当我们声明共享变量为voltile后,对这个变量的读/写将会很特别。理解volatile特性的一一个好方法是把对voltile变量的单个读/写,看成是使用同一一个锁对这些单个读/写操作做了同步。下面我们通过具体的示例来说明,请看下面的示例代码:

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

假设有多个线程分别调用上面程序的三个方法,这个程序在语义上和下面程序等价:

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

如上面示例程序所示,对一个volatile变量的单个读/写操作,与对一个普通变量的读/写操作使用同一个锁来同步,它们之间的执行效果相同。

锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

  • 锁的释放获取建立的happens before关系
  • 锁释放和获取的内存语义
  • 锁内存语义的实现
  • concurrent包的实现

锁的释放获取建立的happens before关系

锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。下面是锁释放获取的示例代码:

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

假设线程A执行writer(方法,随后线程B执行reader(方法。根据happensbefore规则,这个过程包含的happens before关系可以分为两类:

4.根据程序次序规则, 1 happens before 2, 2 happens before 3; 4 happensbefore 5, 5 happens before 6。

5.根据监视器锁规则, 3 happens before 4。

6.根据happens before的传递性, 2 happens before 5。

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

FINAL

  • 写final域的重排序规则
  • 读final域的重排序规则
  • 如果final域是引用类型
  • 为什么final引用不能从构造函数内“逸出
  • final语义在处理器中的实现
  • JSR-133为什么要增强final的语义

final

与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问。对于final域,编译器和处理器要遵守两个重排序规则:

1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序

2.初次读一个包含 final域的对象的引用,与随后初次读这个final 域,这两个操作之间不能重排序。

下面,我们通过一些示例性的代码来分别说明这两个规则:

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

总结

  • 处理器内存模型
  • JMM ,处理器内存模型与顺序-致性内存模型之间的关系
  • JMM的设计
  • JMM的内存可见性保证
  • JSR-133对旧内存模型的修补

处理器内存模型

顺序一致性内存模型是一个理论参考模型, JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照。JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序一致性模型来实现处理器和JMM ,那么很多的处理器和编译器优化都要被禁止,这对执行性能将会有很大的影响。根据对不同类型读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为下面几种类型:

1.放松程序中写 读操作的顺序,由此产生了total store ordering内存模型(简称为TSO)。.

2. 在前面1的基础上,继续放松程序中写-写操作的顺序,由此产生了partialstore order内存模型(简称为PSO )。

3. 在前面1和2的基础上,继续放松程序中读-写和读读操作的顺序,由此产生了relaxed memory order内存模型(简称为RMO )和PowerPC内存模型。

注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因为处理器要遵守as- if serial语义,处理器不会对存在数据依赖性的两个内存操作做重排序)。

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

跟着腾讯14级工程师,学习优秀程序员必须了解的Java内存模型

 

资料全部真实有效,不会缺页,不会只是大纲!!!由于文章篇幅有限,为了不耽误大家宝贵的时间,这份PDF小编已经为大家整理好了,需要免费领取的朋友麻烦帮忙转发、转发、转发这篇文章(一定要转发哦),然后私信小编【学习】或【进阶】免费获取。