Java内存模型(MESI、内存屏障、volatile和锁及final内存语义)
JMM (Java内存模型)
Java线程的实现
实现线程主要有三种方式,Java线程从JDK1.3后采用第一种方式实现:
- 使用内核线程实现(1:1实现)
- 使用用户线程实现(1:N实现)
- 使用用户线程加轻量级进程混合实现(N:M实现)
- KTL: 内核线程
- LWP:轻量级进程
- UT:用户线程
线程之间通信机制
Java并发采用的是共享内存模型
- 共享内存
- 消息传递
重排序
包括:
- 编译器优化的重排序。
- 指令级并行的重排序。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
- 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行
内存屏障
内存屏障类型:
StoreLoad Barries的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能
缓存一致性(MESI协议)
https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers
状态:
- 修改(modified):仅当前处理器拥有该缓存行,并且缓存行被修改过了,一定时间内会写回主存,会写成功状态会变为S。
- 独占(exclusive):仅当前处理器拥有该缓存行,并且没有修改过,是最新的值。
- 共享(share):有多个处理器拥有该缓存行,每个处理器都没有修改过缓存,是最新的值。
- 失效(invalid):缓存行被其他处理器修改过,该值不是最新的值,需要读取主存上最新的值。
MESI | M | E | S | I |
---|---|---|---|---|
M | X | X | X | √ |
E | X | X | X | √ |
S | X | X | √ | √ |
I | √ | √ | √ | √ |
这个图的含义就是当一个core持有一个cacheline的状态为Y时,其它core对应的cacheline应该处于状态X, 比如地址 0x00010000 对应的cacheline在core0上为状态M, 则其它所有的core对应于0x00010000的cacheline都必须为I
状态转换:
- Red: Bus initiated transaction.
- Black: Processor initiated transactions.
处理器请求:
- PrRd: 处理器请求读Cache block
- PrWr: 处理器请求写Cache block
总线请求:
- BusRd:由一个处理器向另一个处理器发出的缓存读请求
- BusRdX:由一个缓存中没有该变量的处理器向另一个处理器发送的缓存写请求
- BusUpgr:由一个缓存中拥有该变量的处理器向另一个处理器发送的缓存写请求
- Flush:有一个处理器将变量写回了主存
- FlushOpt:通知别的处理器修改变量的值(缓存间传值)
示例:
Happens-Before
- 某个线程中的每个动作都happens-before该线程中该动作后面的动作
- 某个管程上的unlock动作happens-before同一个管程上后续的lock动作
- 对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作
- 在某个线程对象上调用start()方法happens-before该启动了的线程中的任意动作
- 某个线程中的所有动作happens-before任意其它线程成功从该线程对象上join()中返回
- 如果某个动作a happens-before动作b,且b happens-before动作c,则有a happens-before c
volatile字段内存语义
happens-before规则
对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作
特性:
- 可见性: 对任意单个volatile变量的读,总是能看到(任意线程)对该volatile最后的写入
- 原子性: 对任意单个volatile变量的读/写具有原子性,但类似与volatile++这种复合操作不具有原子性
内存语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存
内存语义的实现:volatile修饰的变量,会多一个lock指令,这个操作的作用相当于一个内存屏障。
保守策略的JMM内存屏障插入策略:
- 在每个volatile写操作的前面插入一个StoreStore屏障
- 在每个volatile写操作的后面插入一个StoreLoad屏障
- 在每个volatile读操作的后面插入一个LoadLoad屏障
- 在每个volatile读操作的后面插入一个LoadStore屏障
锁的内存语义
happens-before规则
某个管程上的unlock动作happens-before同一个管程上后续的lock动作
内存语义
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
ReentrantLock
CAS实现
synchronized
ACC_SYNCHRONIZED方法访问标识或monitor enter、monitor exit指令实现
final字段内存语义
两个排序规则:
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序
- JMM禁止编译器把final域的写重排序到构造函数之外
- 编译器会在final域的写之后,构造函数return之前,插入一个storestore屏障,这个屏障禁止处理器把final域的写重排序到构造函数之外。
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序
JSR-133对旧内存模型的修补
- 增强volatile的内存语义。旧内存模型运行volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序。
- 增强final的内存语义。在旧内存模型,多次读取同一个final变量的值可能会不同。为此,JSR-133为final增加了两个重排序规则,在保证fianl引用不会从构造函数内逃逸出的情况下,final具有了初始化安全性。
参考:
- 《深入理解Java虚拟机》
- 《Java并发编程的艺术》
- https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers
- https://www.jianshu.com/p/6745203ae1fe
- https://www.jianshu.com/p/a2b3f3d754ec?utm_campaign=haruki
- https://www.beichengjiu.com/mathematics/178273.html
Java内存模型(MESI、内存屏障、volatile和锁及final内存语义)的更多相关文章
- 深入理解Java内存模型(四)——volatile
volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...
- 【转】深入理解Java内存模型(四)——volatile
volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...
- JAVA内存模型与线程以及volatile理解
Java内存模型是围绕在并发过程中如何处理原子性.可见性.有序性来建立的. 一.主内存与工作内存 Java内存模型主要目标是在虚拟机中将变量存储到内存和从内存中取出变量.这里的变量包括:实例字段.静态 ...
- Java内存模型(三)原子性、内存可见性、重排序、顺序一致性、volatile、锁、final
一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量co ...
- java内存模型(线程,volatile关键字和sychronized关键字)
volatile关键字 用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况. ...
- Java内存模型(二)volatile底层实现(CPU的缓存一致性协议MESI)
CPU的缓存一致性协议MESI 在多核CPU中,内存中的数据会在多个核心中存在数据副本,某一个核心发生修改操作,就产生了数据不一致的问题,而一致性协议正是用于保证多个CPU cache之间缓存共享数据 ...
- Java并发编程:JMM(Java内存模型)和volatile
1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性.可见性和有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.1. 原子性 原子性:即一个或多个操作要么全部 ...
- 07 volatile & java 内存模型
一 从单例模式说起 在singleton 单例模式一文中我们详细了解Java中单例模式的实现,不了解的可以先阅读之. 在该文最后我们给出了双重校验锁来保证既实现线程安全,又能够使性能不受很大的影响的单 ...
- Java内存模型中volatile关键字的作用
volatile作用总结: 1. 强制线程从公共内存中取得变量的值,而不是从线程的私有的本地内存中,volatile修饰的变量不具有原子性(修改一个变量的值不能同步). 2. 保证volatile修饰 ...
随机推荐
- python菜鸟教程学习5: python运算符
算术运算符: 整除符号//:向下取接近商的整数 加+ 减- 乘* 除/ 取余% 幂** 比较运算符: 等于== 不等于!= 大于> 小于< 大于等于>= 小于等于<= 赋值运算 ...
- 使用python统计《三国演义》小说里人物出现次数前十名,并实现可视化。
一.安装所需要的第三方库 jieba (jieba是优秀的中文分词第三分库) pyecharts (一个优秀的数据可视化库) <三国演义>.txt下载地址(提取码:kist ) 使用pyc ...
- 面试阿里,字节跳动99%会被问到的java线程和线程池,看完这篇你就懂了!
前言: 最近也是在后台收到很多小伙伴私信问我线程和线程池这一块的问题,说自己在面试的时候老是被问到这一块的问题,被问的很头疼.前几天看到后帮几个小伙伴解决了问题,但是问的人有点多我一个个回答也回答不过 ...
- FL Studio20效果器Fruity Reverb 2功能介绍
FL Studio,也就是我梦通常所说的水果音乐制作软件,是一款新手就可以用的软件.其操作简单,界面简洁大方,就算只用鼠标也可以轻松编曲. FL Studio20中有许多自带合成器是很好用的,同时也是 ...
- Linux中的基本命令无法使用,报Command not found的错误的解决方法
一般我们在Linux中执行命令的时候,会报 Command not found 的错误,报这种错误一般有两种原因:一是你的系统中没有安装这个命令,需要你手动安装,另外一种原因就是平常这些命令用着好好的 ...
- JavaSE 学习笔记05丨泛型、集合
Chapter. 10 泛型 10.1 泛型程序设计 泛型,指可以在类或方法中预支地使用未知的类型.泛型程序设计(Generic programming),意味着编写的代码可被很多不同类型的对象所重用 ...
- MySQL的两种日志类型,redo log,binlog
文章内容学习:极客时间-林晓彬老师-MySQL实战45讲 整理而得 我们知道MySQL数据库在发生意外宕机的情况下,可以将数据恢复到历史的某个时间点,能实现这个功能依靠的是日志,MySQL提供两种类型 ...
- 企业安全06-Fastjson-CNVD-2017-02833
Fastjson-CNVD-2017-02833 一.漏洞概述 fastjson在解析json的过程中,支持使用@type字段来指定反序列化的类型,并调用该类的set/get方法来访问属性,当组件开启 ...
- Spring源码解析之BeanFactoryPostProcessor(二)
上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDef ...
- GoFrame 模板引擎对变量转义输出- XSS 漏洞
GoFrame 模板引擎对变量转义输出- XSS 漏洞 环境: gf v1.14.4 go 1.11 官网说明 默认情况下,模板引擎对所有的变量输出并没有使用HTML转码处理,也就是说,如果开发者处理 ...