第3章| Java内存模型——02顺序一致性

重排序

数据依赖性:如果两个操作访问同一个变量,且其中有一个为写操作,这时这两个操作存在数据依赖性。
Note:数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间以及不同线程之间的数据依赖性不被编译器和处理器考虑。

as-if-serial语义:不管如何重排序,(单线程)程序的执行结果不能被改变;编译器、runtime和处理器都必须遵守这个语义。

在计算机中,软件和硬件技术的一个共同目标:在不改变程序的执行结果前提下,可以尽量提高并行度

1
2
3
4
if(flag){ // A
int i=1+1; // B
}
这个时候就说A和B之前存在控制依赖

在单线程程序中,对存在控制依赖的操作重排序,不会改变执行结果;但在多线程中,可能会改变程序的执行结果。

顺序一致性

当数据未能正确同步时,就会存在数据竞争现象。数据竞争的定义如下:

  • 在一个线程中写一个变量,
  • 在另一个线程中读同一个变量
  • 而且写和读没有通过读来排序

如果程序是正确同步的,程序的执行将具有顺序一致性(Sequentially Consistent)——即程序的执行结果与在顺序一致性内存模型中的执行结果相同,这里的同步包括常用的同步原语(synchronized、volatile、final)的正确使用。

顺序一致性内存模型

该模型是一个理想化的理论参考模型,为开发人员同提供了极强的内存可见性保证。有以下两大特性:

  1. 一个程序中的所有操作必须按照程序的顺序来执行;
  2. (不管程序是否同步)所有的线程都只能看到一个单一的操作执行顺序。在此模型中,每个操作都必须原子执行并立刻对所有线程可见。
    内存一致性模型的视图
  • 未同步程序在顺序一致性模型中虽然整体执行无序,但所有线程都只能看到一个一致的整体执行顺序(这是因为该模型能保证每个操作对任意线程立即可见)。
  • 未同步程序在JMM中不但整体执行无序,且所有线程看到的操作执行顺序也可能不一样。

临界区概念
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。

JMM具体实现上的方针是:在不改变(正确同步的)程序执行结果的前提下,尽可能地配合编译器和处理器的优化。

未同步程序的执行特性

  1. 对于未同步或未正确同步的多线程程序,JMM只提供最小安全性:线程执行时读取到的值,要么是之前某个线程写入的值,要么是默认值

  2. JMM不保证对64位的long型和double型变量的写操作具有原子性,而顺序一致性模型保证对所有的内存读/写操作都具有原子性。

  3. 总线会同步试图并发使用总线的事务。在一个处理器执行总线事务(处理器和内存之间数据传递的一系列步骤)期间,总线会禁止其他的处理器和I/o设备执行内存的读/写。总线的工作机制如下图:
    总线的工作机制

  4. 注意:jdk5之前,一个64为long/double型变量的读/写操作可以被拆分为两个32位的读/写操作来执行,而jdk5之后只允许将一个64为long/double型变量的操作拆分为两个32位的操作来执行

码哥 wechat
欢迎关注个人订阅号:「码上行动GO」