在Java中的指令重新sorting&发生之前的关系

在“Java并发实践”一书中,我们多次被告知,我们程序的指令可以由编译器,JVM在运行时甚至处理器进行重新sorting。 所以我们应该假定被执行的程序不会按照我们在源代码中指定的顺序执行它的指令。

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

  • “程序顺序规则。线程中的每个动作都发生在该线程中的每个动作之前,这些动作在程序顺序中稍后进行。

我相信“程序顺序”是指源代码。

我的问题是 :假设这条规则,我想知道什么样的指令可能会被重新sorting。

“行动”定义如下:

Java内存模型是根据操作来指定的,包括对variables的读写操作,监视器的locking和解锁,以及与线程的启动和连接。 JMM定义了在程序中的所有操作之前发生的部分sorting。 为了保证执行动作B的线程可以看到动作A的结果(不pipeA和B是否出现在不同的线程中),在A和B之间的关系之前必定有一个事件发生。如果在两个操作,JVM可以自由地对其进行重新sorting。

其他提到的订单规则是:

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

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

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

T1:

x = 5; y = 6; 

T2:

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

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

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

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

编辑

你似乎错误地解释了程序的含义。 程序订单规则归结为 :

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

发生之前在JMM中有一个非常具体的含义。 特别是,从挂钟的angular度来看,并不意味着y=6必须在T1的x=5之后。 这只意味着T1执行的动作顺序必须该顺序一致 。 你也可以参考JLS 17.4.5 :

应该指出的是,两个行动之间存在一个“先发生”的关系并不一定意味着他们必须按照执行的顺序进行 。 如果重新sorting产生符合法律执行的结果,则不是非法的。

在上面给出的例子中,你会同意从T1的angular度(即在一个单线程程序中), x=5;y=6;y=6;x=5; 因为你不读取值。 下一行的声明在T1中保证看到这两个行为,不pipe它们的执行顺序如何。