指令重新排序&发生在java之前的关系之前

时间:2013-04-25 11:23:37

标签: java multithreading concurrency java-memory-model

在Java Concurrency In Practice一书中,我们被告知可以通过编译器,JVM在运行时甚至由处理器重新排序程序的指令。因此,我们应该假设执行的程序的执行顺序与我们在源代码中指定的顺序完全相同。

然而,讨论Java内存模型的最后一章提供了发生在之前规则的列表,指出了JVM保留哪些指令排序。第一条规则是:

  • “程序顺序规则。线程中的每个操作都发生在程序顺序后面的该线程中的每个操作之前。”

我认为“程序顺序”是指源代码。

我的问题:假设这条规则,我想知道哪些指令可能实际重新排序。

“行动”定义如下:

  

Java内存模型是根据操作指定的,包括对变量的读写,监视器的锁定和解锁,以及启动和连接线程。 JMM定义了在程序中的所有操作之前调用的部分排序。为了保证执行动作B的线程可以看到动作A的结果(A和B是否出现在不同的线程中),必须在A和B之间的关系之前发生。在没有发生之前在两个之间进行排序操作,JVM可以随意重新排序。

提到的其他订单规则是:

  • 监控锁定规则。监视器锁定上的解锁发生在同一监视器锁定的每个后续锁定之前。
  • 易变变量规则。写入易失性字段发生在每次后续读取同一字段之前。
  • 线程启动规则。在线程上调用Thread.start会在启动线程中的每个操作之前发生。
  • 线程终止规则。线程中的任何操作都发生在任何其他线程检测到该线程已终止之前,无论是从Thread.join成功返回还是Thread.isAlive返回false。
  • 中断规则。另一个线程上的线程调用中断发生在被中断的线程检测到中断之前(通过抛出InterruptedException,或者调用isInterrupted或中断)。
  • 终结者规则。对象的构造函数的结束发生在该对象的终结器的开始之前。
  • 传递性。如果A发生在B之前,B发生在C之前,那么A发生在C之前。

1 个答案:

答案 0 :(得分:54)

程序订单规则的关键点是: 在一个帖子中

想象一下这个简单的程序(所有变量最初为0):

T1:

x = 5;
y = 6;

T2:

if (y == 6) System.out.println(x);

从T1的角度来看,执行必​​须与在x(程序顺序)之后分配的y一致。但是从T2的角度来看,并非如此,T2可能会打印0。

实际上允许T1首先分配y,因为2个分配是独立的,交换它们不会影响T1的执行。

通过适当的同步,T2将始终打印5或不打印。

修改

你似乎误解了程序顺序的含义。 The program order rule boils down to

  

如果xy是同一个帖子的操作,并且x按照程序顺序排在y之前,那么hb(x, y)(即x } 发生在 y之前。

发生 - 之前在JMM中具有非常特殊的含义。特别是,从挂钟的角度来看,意味着y=6必须在T1 x=5之后。它只表示T1执行的操作序列必须与该命令一致。您也可以参考JLS 17.4.5

  

应该注意的是,两个行为之间存在先发生关系并不一定意味着它们必须在实施中以该顺序发生。如果重新排序产生的结果与合法执行一致,则不是非法的。

在上面给出的示例中,您将同意从T1的角度(即在单线程程序中),x=5;y=6;y=6;x=5;一致,因为您没有读取值。在T1中,保证下一行的声明,以查看这两个动作,无论它们的执行顺序如何。