Java内存模型
2021-07-08 19:22:04 1 举报
AI智能生成
登录查看完整内容
Java内存模型
作者其他创作
大纲/内容
顺序一致性
重排序
数据依赖性
定义
两个操作访问同一个变量
其中一个为write
类型
write 后 read
a = 1; b = a;
write 后 write
a = 1; a = 2;
read 后 write
a = b; b = 1;
不重排序
编译器和CPU
as-if-serial
不管怎么重排序,不改变结果
eg.
重排序.png
可能顺序
A->B->C
B->A->C
程序顺序规则
A happens-before BB happens-before CA happens-before C
B 可以排在A 之前执行
happens-before规则
A 执行结果对B 可见
A 按顺序排在B 之前
重排序影响
重排序2.png
分析
1与2重排序
3与4重排序
猜测(Speculation)执行
克服控制相关性
条件为true
写入变量 i
数据竞争与顺序一致性
数据竞争定义
在线程A 中写变量 i
在线程B 中读变量 i
写和读没有同步控制
JMM保证
正确同步下,程序的执行具有顺序一致性
顺序一致性内存模型
目的
内存可见性保证
特性
单线程,顺序执行
操作原子执行
立刻对所有线程可见
JMM未保证
同步程序的顺序一致性效果
synchronized,writer/reader 方法
正确同步下,按顺序串执行
JMM设计方针
在不改变程序执行结果前提下,尽可能优化编译器和CPU
未同步程序的执行特性
JMM最小安全性
之前设置的值
默认值(0、null、false)
JMM特性差异
不保证单线程内的操作按顺序执行
不保证所有线程能看到一致的操作执行顺序
不保证对64位long 和double 写的原子性
JMM基础
关键问题
如何通信
共享内存
消息传递
如何同步
抽象结构
堆内存
实例域
静态域
数组
栈
局部变量
方法参数
异常处理器参数
编译器优化
指令级并行
内存系统
内存屏障
LoadLoad Barriers
Load1;LoadLoad;Load2
装载Load1 先于Load2
StoreStore Barriers
Store1;StoreStore;Store2
写内存Store1(其他CPU可见)先于Store2
LoadStore Barriers
Load1;LoadStore;Store2
装载Load1 先于Store2
StoreLoad Barriers
Store1;StoreLoad;Load2
写内存Store1 先于Load2
happens-before
单线程下的每个操作,happens-bofore 任意后续操作
监视器锁规则
解锁,happens-before 加锁
volatile 变量规则
对一个volatile 域写,happens-before 后续的读
传递性
注意
A 必须在B 之前执行?
真实
Java内存模型
同步原语
volatile 的内存语义
可见性、原子性
volatile 写-读
建立的happens-before
volatile01.png
根据程序次序规则,1 happens-before 2;3 happens-before 4
根据volatile 规则,2 happens-before 3
根据传递规则,1 happens-before 4
内存语义
volatile 写的内存语义
volatile-写.png
当写一个volatile 变量时,JMM 会把该线程的【本地内存】中的共享变量--刷新-->【主内存】
volatile 读的内存语义
volatile-读.png
当读一个volatile 变量时,JMM会把该线程的【本地内存】设置无效,再从【主内存】中读取共享变量
等效
锁的释放-获取的内存效果
volatile 写 = 锁的释放
volatile 读 = 锁的获取
实现
volatile 写
前
StoreStore 屏障
后
StoreLoad 屏障
volatile 读
后(1)
LoadLoad 屏障
后(2)
LoadStore 屏障
锁的内存语义
临界区互斥执行
发送消息
释放锁的线程--向-->获取同一个锁的线程
释放/获取的happens-before 关系
锁01.png
根据程序次序规则
1 happens-before 22 happens-before 34 happens-before 55 happens-before 6
根据监视器锁规则
3 happens-before 4
根据传递性
2 happens-before 5
释放/获取的内存语义
释放
JMM会把该线程的【本地内存】中的共享变量--刷新-->【主内存】
获取
JMM会把该线程的【本地内存】设置无效,再从【主内存】中读取共享变量
线程A 释放锁,线程B 再获取这个锁,线程A 通过主内存向线程B 发送消息
方式
利用volatile 变量的write/read 所具有的内存语义
利用CAS 所附带的 volatile 读/写内存语义
ReentrantLock分析
锁最后都要写一个volatile 变量state
公平锁获取时,首先去读取volatile 变量
非公平锁获取时,首先会用CAS 更新volatile 变量
concurrent 包的实现
Java 线程间通信
线程A 写volatile 变量
线程B 读这个volatile变量
线程B 用CAS更新这个volatile变量
线程A 更新volatile变量
线程B 读这个volatile 变量
线程B 用CAS 更新这个volatile 变量
模式
声明共享变量volatile
使用CAS 更新实现线程间同步
配合volatile 的读/写 和 CAS 的内存语义,实现线程间通信
final 域的内存语义
重排序规则
构造函数内对final 域的写入,被创建的对象引用赋值给变量
初次读一个包含final 域的对象,随后初次读这个final 域
write-重排序规则
JMM 禁止把final 域的write,重排序到构造函数外
final 域write 之后,构造函数return 之前,插入StoreStore 屏障
保证:在对象引用被任意线程可见之前,final 域已正确初始化
read-重排序规则
初次read 对象引用,初次read 该对象的final 域,JMM禁止CPU 重排序
编译器在读 final 域前,插入一个LoadLoad 屏障
保证:在读一个对象的final 域之前,一定先读对象
final 域为引用类型
构造器内对final 引用对象的成员域写入,被创建的对象引用赋值给变量
对象引用“逸出”
final 域还没有初始化
设计原理
JMM设计
程序员对JMM 的使用
强内存模型
编译器和CPU 对JMM 的使用
弱内存模型
改变结果
JMM 禁止
不变结果
JMM 允许
如果操作A happens-before 操作B
操作A 的结果对B 可见
操作A 顺序排在B 之前
如果操作之间存在happens-before 关系
只要执行结果不变,允许重排序
规则
start() 规则
线程A 执行ThreadB.start(),start() happens-before 线程B 的操作
join() 规则
线程A 执行ThreadB.start(),线程B 的操作happens-before 线程A 从join() 操作成功返回
双重检查Lock
问题根源
instance = new Instance(); 指令重排序
解决方案
volatile 域
类的初始化
0 条评论
回复 删除
下一页