目录
1. JMM基础
1.1 并发编程的两个关键问题
1.1.1 线程之间如何通信
通信机制:共享内存(Java的并发采用的是共享内存模型);消息传递
在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态来隐式通信;
在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式通信。
1.1.2 线程之间如何同步
同步:程序中用于控制不同线程间操作发生相对顺序的机制。
在共享内存的并发模型里,同步是显式进行的,必须显式指定某个方法或某段代码需要在线程之间互斥执行;
在消息传递的并发模型里,消息的发送必须在消息的接收之前,因此同步是隐式进行的。
1.2 JMM抽象结构
堆内存在线程之间共享,实例域、静态域、数组元素都存储在堆内存中。Java线程之间通信由JMM控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系如下图所示:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。
线程A向线程B发消息的流程如下,起初,本地内存A、本地内存B、主内存中x的值都为0。
JMM通过控制主内存与每个线程的本地内存之间的交互,提供内存可见性保证。
1.3 从源代码到指令序列的重排序
重排序:编译器和处理器为了提高性能对指令进行重排序的一种优化措施。
从Java源代码到最终实际执行的指令序列,会分别经过如下3种排序
1:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
2:指令级并行技术将多条指令重叠执行,如果不存在数据依赖性,处理器可以改变语句对应及其指令的执行顺序
3:由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行
这些重排序可能会导致多线程程序出现内存可见性问题,JMM通过禁止特定类型的编译器和处理器提供一致的内存可见性保证。
对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序;
对于处理器,JMM的处理器重排序规则会要求Java编译器在生成指令序列时,插入特定类型的内存屏障指令,来禁止特定类型的处理器重排序。