http://www.bubuko.com/infodetail-1627377.html
http://www.bubuko.com/infodetail-1627292.html
3.1内存模型基础
线程通信
消息传递(显式),共享内存(隐式)
线程同步
消息传递(隐式),共享内存(显式)
3.1.2内存模型的抽象结构
JMM定义了各线程和主内存之间的抽象关系。决定了一个线程对于共享变量的写入何时对另一个线程可见。
本地内存包含了缓存,写缓冲区,寄存器,以及其他的硬件和编译器优化。
3.1.3从源代码到指令序列的重排序
编译器重排序(编译器级别)
指令级并行重排序(处理器级别) 指令并行技术的使用使得指令出现了重排序
内存系统的重排序 (处理器级别) cpu可能使用了读/写缓冲区,这使得加载和存储操作看上去是在乱序的执行
对于编译器,JMM排序规则会禁止特定类型的编译器重排序
对于处理器,JMM的处理器排序规则会要求JAVA编译器在生成指令序列时,插入特定类型的内存屏障(Memory fence)指令,通过内存屏障指令来禁止特定类类型的处理器
重排序。
3.1.4并发编程模型的分类
1、为什么使用写缓存区
写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区
中对同一内存地址的多次写,减少对内存总线的占用。每个处理器之间的写缓存区是相互隔离的。使用写缓冲区导致处理器对内存的执行顺序和真正的内存执行顺序是不一
致的。
2、x86系列因为处理器的内存模型较强,这款处理器只允许对写-读操作做重排序。
3、内存屏障指令
第四个内存屏障指令,具有其他三个所有的效果,一般所有的cpu都支持该内存屏障,其他的三个可能不支持。该屏障需要刷新写缓冲区的所有数据到内存里,所以只需开销
较大。
3.1.5 happens-before简介
1、什么是happens-before
前面的操作的执行结果对后面的操作可见
2、哪些happens-before
1、程序顺序规则
2、监视器锁规则
3、volitale变量规则 对一个volatile变量的写,happens-before于任意后续的对volatile域的读。
4、传递性
3、一个happens-before规则对应于一个或者多各编译器和处理器重排序规则。
3.2重排序
为什么需要重排序
目标都是为了提高效率,可以从编译器和处理器级别的重排序分析
3.2.1数据依赖性
1、什么是数据依赖性
如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:
名称 | 代码示例 | 说明 |
写后读 | a = 1;b = a; | 写一个变量之后,再读这个位置。 |
写后写 | a = 1;a = 2; | 写一个变量之后,再写这个变量。 |
读后写 | a = b;b = 1; | 读一个变量之后,再写这个变量。 |
上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。
2、编译器和处理器级别的重排序不会改变存在数据依赖关系的两个操作的执行顺序
3、编译器和处理器级别的重排序不会考虑多线程和多处理器之间的数据依赖性
3.2.4重排序对多线程的影响
依赖重排序和非依赖重排序都会对最后的执行结果有影响。
3.3顺序一致性
3.3.1 什么是数据竞争
多个线程同时对一个变量读写,而且对变量的读写没有采取同步机制(final,volatile等)
3.3.2顺序一致性模型
JMM本质
1、处理器内存模型:处理器级别,一般内存模型越弱。因为内存模型越弱,受到的约束越少,这样才可能有更多的优化来提高性能。、
顺序一致性模型:理论参考模型,约束较强。
JMM:语言级内存模型。程序员直接相关的是该级别的内存模型,JMM对程序员暴漏的是一些happens-before原则
2、JMM需要处理好JMM与处理器模型的差异性,办法就是插入内存屏障,给处理器施加一些限制,从而保证JMM的一致性。
本节主要讲的是
1、JMM为了保证一致性,在处理器重排序和编译器重排序上面临的挑战。
2、JMM通过插入内存屏障和禁止一些编译器排序来协调JMM和处理器内存模型。
3、JMM的一些happens-before原则