综述:
简单的说,Java的内存模型定义了在一个线程对一个共享变量进行修改后,修改后的共享变量什么时候对其它线程可见。
作用:
-
对于程序员
JMM给程序员呈现出来的是具有顺序一致性的强内存模型。(通俗点就是所见即所得)
-
对于处理器与编译器
JMM给处理器与编译器提供了一个比较弱的happens-before内存模型,这样尽可能多的给处理器与编译器优化代码的空间。
处理器与编译器的优化技术
-
编译器优化的重排序。
编译器在不改变单线程程序语义的前提下,可以对我们编写的代码进行重排。如下:
public class Test {
public static void main(String args []) {
int a = 1; // 1
int b = 2; // 2
// 在单线程语义下这个程序的输出为 1
// 交换标注为 1,2 的两行代码不改变单线程程序语义
// 编译器可以对这样的代码进行指令重排,以提高程序运行的性能
System.out.println(a);
}
}
public class Test {
public static void main(String args[ ]) {
int a = 0; // 1
a = 2; // 2
a = 3; // 3
// 在单线程语义下这个程序的输出为3
// 如果标注为 2 ,3的代码进行交换顺序,那就改变了单线程语义,JMM
// 是禁止这种重排序的。
System.out.println(a);
}
}
2 . 处理器的指令级并行重排序
现代处理器采用了指令级并行技术,来对多条指令进行重排序。如果指令间不存在数据依赖(<u>对同一个变量写后读,读后写,写后写</u>),那处理器就可以改变指令间的执行顺序。如下:
0x00007f31c9108ac0: mov %eax,-0x14000(%rsp)
0x00007f31c9108ac7: push %rbp
0x00007f31c9108ac8: sub $0x30,%rsp
0x00007f31c9108acc: movabs $0x7f31c8c00448,%rdi ; {metadata(method data for {method} {0x00007f31c8c00260} 'test' '()V' in 'testTwo')}
0x00007f31c9108ad6: mov 0xdc(%rdi),%ebx
// 假设以上5条汇编指令之间不存在数据依赖,如果处理器的流水线大于等于5,那么,这5条汇编指令可以并行的进行计算。
3 . 内存系统的重排序
由于处理器使用缓存,这使得加载和存储操作看上去是在乱序执行。如图[1]:
intel i7 处理器是4核8线程,每个核心都有自己的L1, L2 缓存,当不同的核心缓存相同的共享变量,并写回到L3 缓存时,就存在内存系统的重排序。
Happens-before 规则
综述:happens-before 关系是由Lamport(1978)这位大神提出的。它的表述为: a -> b 读作“a 在 b 之前发生”,意思是所有的进程(分布式系统中)/ 线(我们现在讨论的JMM)一致认为事件 a 先于事件 b 发生:*注意:这里的先发生实际上是一种可见性的表述,它并不代表在物理时间上事件a先于事件b发生,而是事件b在执行前要能看到事件a执行后的结果。*这就为处理器,与编译器的重排提供了保证。
我们来看看JSR-133对JMM中happens-before规则的示例:
1. 程序顺序规则:一个线程中的的每个操作,happens-before于该线程中的任意后续操作。
2. 监视器规则: 对一个锁的解锁,happens-before于随后对这个锁的加锁。
3. volatile变量规则:对一个volatile域的写,happens-before与任意后续对这个volatile域的读。
4. 传递性: 如果 a -> b, b -> c, 则 a -> c.
5. 线程启动 start() 规则:如果线程A 执行操作ThreadB.start()(启动线程B),那么A线程的启动线程的操作,happens-before于线程B中的任意操作。
6. join()规则: 如果线程A 执行ThreadB.join()并成功返回,那么线程B中的任意操作,happens-before 于线程A 从ThreadB.join()操作成功返回。
顺序一致性内存模型
综述:顺序一致性模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了内存可见性的保证。
顺序一致性的两大特征:
1. 一个线程中所有操作必须按照程序的顺序来执行
2.不管程序是否同步,所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且对所有线程立即可见。
本博客是对《JAVA并发编程的艺术》方腾飞,魏鹏,程晓明 著。第三章的读后感。
参考资料:[1] intel开发技术手册卷3第2章