Java内存模型-JMM
并发编程
- 在并发编程中需要考虑2个关键问题:线程之前如何通信?线程之前如何同步?
- 通信:线程之间通过何种机制来交换信息
- 同步:程序用于控制不同线程之间操作发生发生相对顺序的机制
- 并发模型
- 共享内存:
- 隐式通信:线程之间通过写-读内存中的公共状态来隐式的进行通信
- 显式同步:程序员必须显式的某个方法或某段代码需要在线程之间互斥进行
- 消息传递:
- 显式通信:线程之间必须通过明确的发送消息来显式的进行通信
- 隐式同步:消息的发送必须在消息接收之前
- 共享内存:
- 三大特性:
- 原子性:一个操作或者多个操作要么全部执行要么全部不执行;
- 可见性:当多个线程同时访问一个共享变量时,如果其中某个线程更改了该共享变量,其他线程应该可以立刻看到这个改变;
- 有序性:程序的执行要按照代码的先后顺序执行;
Java内存模型–JMM
-
JMM:Java虚拟机规范中试图定义一种java内存模型,来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果;
-
定义了些啥:
-
主内存与工作内存:
- 所有变量都存储在主内存;
- 每个线程都有自己的工作线程;
- 线程的工作内存中保存了所需变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中,线程间变量的值需要通过主内存传递;
-
内存见交互操作:
- 定义了主内存与工作内存之间具体的交互协议
- 8种原子操作:lock、unlock、read、load、use、assign、store、write
- 定义了这8中操作的一些规则
-
对于volatile变量的一些特殊规则
-
对于long和double型的变量的特殊规则
- 允许虚拟机实现可以选择不保证64位数据类型的load、store、read、write的原子性
-
备注:规则的具体内容自行参看《深入理解java虚拟机》第12章的内容
-
原子性、可见性、有序性
-
原子性:
- 直接保证原子性的变量操作:read、load、use、assign、store、write
- 提供字节码指令:monitorenter、monitorexit来隐式的使用lock、unlock这两个指令(synchronized)
-
可见性:
- 指当一个线程修改了共享编程的值,其他线程能够立即得知这个修改
- volatile、synchronized、final
-
有序性:
-
在本线程内观察所有的操作都是有序的;在一个线程观察另一个线程,所有的操作都是无序的;
-
前半句:线程内变现为串行的语义(As-If-Serial)
-
后半句:“指令重排序”现象和“工作内存与主内存同步延迟”现象
-
volatile和synchronized
-
重排序
-
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三类:
- 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
- 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
- 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行
Happens-Before
- “先行发生”原则
- 它是用来判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则,我们可以解决在并发环境下两个操作之间是否存在冲突的问题
- 与程序员密切相关的 happens-before 规则如下:
- 一个线程中的每个操作,happens-before 于该线程中的任意后续操作
- 监视器锁规则:对一个监视器的解锁,happens-before 于随后对这个监视器的加锁
- volatile 变量规则:对一个 volatile 域的写,happens-before 于任意后续对这个 volatile 域的读
- 传递性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C
- 注意:两个操作之间具有 happens-before 关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before 仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前
REF
《深入理解java虚拟机》
https://www.javazhiyin.com/15432.html