初窥Java内存模型(JMM)
《Java并发编程的艺术》书籍
在并发编程中存在两个问题:
- 线程之间如何通信
- 线程之间如何同步
一、JMM抽象结构
用来控制多线程之间的通信,可见性、有序性、原子性。
线程A如何与线程B进行通信
线程之间并没有直接通信通道,如图所示要想实现通信需经历:
1)将本地内存A中更新的变量刷新到主内存当中
2)线程B到主内存读取
二、重排序
为什么会进行重排序?简单来说就是提高性能。
有三种类型
1)编译器优化重排序
2)指令级并行的重排序
3)内存系统的重排序
1、重排序带来的问题
在多线程当中重排序可能会改变程序的执行结果,通过在生成指令序列时,插入特定类型的内存屏障指令来禁止重排序。
1.1内存屏障类型
1.2happens-before
在Java并发编程艺术这本书中写到,happens-before的概念是用来阐述操作之间的内存可见性。
2、数据依赖关系与控制依赖关系
2.1 什么是数据依赖关系
如果两个操作访问同一个变量,这两个操作中有一个为写操作,此时这两个操作就存在数据依赖关系。有三种类型,写后读、写后写、读后写。在单线程中执行时重排序会遵守数据依赖,编译器和处理器不会改变存在数据依赖关系的两个操作之间的执行顺序。
2.2 什么是控制依赖关系
在判断语句当中例如下列伪代码中,操作1和操作2之间存在着数据控制依赖关系。当存在控制依赖关系时,会影响指令序列执行的并行度。因此编译器和处理器会采用猜测执行来克服控制相关性对并行度的影响。具体来说,处理器可以提取读取和计算i*i,然后临时保存到一个名为重排序缓冲(Record Buffer,ROB)的硬件缓存中,当条件判断为真是,写入到a中。
if(flag){//操作1
a = i*i;//操作2
}