重排序

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

数据依赖性

在单线程中,只要重排序两个操作的执行顺序,程序的执行结果就会被改变,称这两个操作具有数据依赖性。

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

as-if-serial语义

不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。

程序顺序规则

如果A happens-before B,JMM并不要求A一定要在B之前执行。JMM仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。JMM允许这种重排序。

重排序对多线程的影响

在单线程程序中,对存在数据依赖的操作重排序,不会改变执行结果(这也是as-if-serial语义允许对存在数据依赖的操作做重排序的原因);但在多线程程序中,对存在数据依赖的操作重排序,可能会改变程序的执行结果。

顺序一致性

顺序一致性内存模型是一个理论参考模型,在设计的时候,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参照。

数据竞争与顺序一致性

当程序未正确同步时,就可能会存在数据竞争。

JMM对正确同步的多线程程序的内存一致性做了如下保证:

如果程序是正确同步的,程序的执行将具有顺序一致性(SequentiallyConsistent)——即程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同。这对于程序员来说是一个极强的保证。

这里的同步是指广义上的同步,包括对常用同步原语(synchronized、volatile和final)的正确使用。

顺序一致性内存模型

两大特性:

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

同步程序的顺序一致性效果

顺序一致性模型中,所有操作完全按程序的顺序串行执行。

而在JMM中,临界区内的代码可以重排序。

JMM会在退出临界区和进入临界区这两个关键时间点做一些特别处理,使得线程在这两个时间点具有与顺序一致性模型相同的内存视图)。临界区内的这种重排序既提高了执行效率,又没有改变程序的执行结果。

未同步程序的执行特性

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

为了实现最小安全性,JVM在堆上分配对象时,首先会对内存空间进行清零,然后才会在上面分配对象(JVM内部会同步这两个操作)。因此,在已清零的内存空间(Pre-zeroed Memory)分配对象时,域的默认初始化已经完成了

JMM不保证未同步程序的执行结果与该程序在顺序一致性模型中的执行结果一致

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

results matching ""

    No results matching ""