java线程之内存模型

2018-03-25  本文已影响0人  dimdark

参考书籍: <<java并发编程的艺术>>
这篇文章是自己阅读该书籍时的读书笔记

1. 并发编程模型的两个关键问题

在并发模型中, 常常要处理两个关键的问题:

常见的通信机制有两种: 共享内存消息传递

共享内存并发模型中, 同步显示进行的;
消息传递并发模型中, 同步隐式进行的;
java的并发采用的是共享内存模型(因此需要显示进行同步)

2. java内存模型的抽象结构
java内存模型的抽象结构示意图
3. 数据依赖性

如果两个操作访问同一个变量, 且这两个操作中有一个为写操作, 此时这两个操作之间就存在数据依赖性

数据依赖类型

注意:

编译器和处理器在重排序时, 会遵守数据依赖性, 编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序;

这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作, 不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑;

4. 重排序

重排序 是指编译器处理器为了优化程序性能而对指令序列进行重新排序的一种手段;

重排序的类型

注意: 内存系统的重排序可能导致处理器对内存的写/读操作的执行顺序不一定与内存实际发生的读/写操作顺序一致

对于编译器的重排序, JMM的编译器重排序规则会禁止特定类型的编译器重排序;
对于处理器的重排序, JMM的处理器重排序规则会要求java编译器在生成指令序列时, 插入特定类型的内存屏障(Memory Barriers)指令, 通过内存屏障指令来禁止特定类型的处理器重排序

内存屏障的类型
JMM这样做的目的是: 为程序员提供一致的内存可见性的保证
5. 顺序一致性内存模型(理论模型 参考模型)

顺序一致性内存模型的两大特性:
- 一个线程中的所有操作必须按照程序的顺序来执行;
- (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序, 在顺序一致性内存模型中, 每个操作都必须原子执行且立刻对所有线程可见;

顺序一致性内存模型为程序员提供的视图

注意: 正确同步的多线程程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同(即可以利用程序在顺序一致性内存模型的执行结果来判断自己编写的多线程程序是否符合期望)

6. volatile 的内存语义
7. 锁的内存语义

注意: 锁释放与volatile写有相同的内存语义; 锁获取与volatile读有相同的内存语义;

8. final 域的内存语义

final 域的重排序规则可以保证在对象引用为任意线程可见之前, 对象的 final 域已经被正确初始化过了

final 域的重排序规则可以保证在读一个对象的 final 域之前, 一定会先读包含这个 final 域的对象的引用

注意: final 的内存语义向程序员保证了只要对象是正确构造的(被构造对象的引用在构造函数中没有"逸出")则不需要使用同步就可以保证任意线程都能看到这个 final 域在构造函数中被初始化之后的值

9. happens-before 规则(JMM对程序员的承诺)

1). 程序顺序规则
一个线程中的每一个操作, happens-before 于该线程中的任意后序操作;

2). 监视器锁规则
对一个锁的解锁, happens-before 于随后对这个锁的加锁;

3). volatile变量规则
对一个volatile域的写, happens-before于任意后续对这个volatile域的读;

4). 传递性
如果A happens-before B, 且B happens-before C, 那么 A happens-before C;

5). start()规则
如果线程A执行操作ThreadB.start()(启动线程B), 那么A线程的ThreadB.start()操作 happens-before 于线程B中的任意操作;

6). join()规则
如果线程A执行操作ThreadB.join()并成功返回, 那么线程B中的任意操作 happens-before 于线程A从 ThreadB.join()操作成功返回(的后序操作);

7). 线程终结规则
线程中所有的操作都先行发生于线程的终止检测;

8). 对象终结规则
一个对象的初始化完成先行于它的finalize()方法的开始;

上一篇 下一篇

猜你喜欢

热点阅读