3

在某些链接中给出了同步块内的代码重新排序是可能的,而其他一些站点则说不可能。您能否举个例子来描述使用同步时代码重新排序的实际情况?

4

2 回答 2

5

有两种可能的重新排序:

  • 同步块之前或之后的语句可以在同步块内移动(当然前提是它们不在不同的同步块中)-有时称为“蟑螂汽车旅馆”原则
  • 同步块中的语句可以重新排序,只要修改是顺序一致的(即,从与块共享发生前关系的其他代码中看不到效果)

但更重要的是:如果您的代码正确同步,这些重新排序不会影响程序的执行,这是您应该关心的。

示例(所有变量最初为 0):

线程1:

a=1;
synchronized(lock) {
    b=1;
    c=1;
}
d=1;

线程2:

synchronized(lock) {
    if (a==1) print(b); // can print 0 or 1
    if (b==1) print(a)/print(c); // always prints 1
    if (b==1) print(d); // can print 0 or 1
    if (d==1) print(a)/print(b)/print(c); // always prints 1
}

特别是,允许​​移动d=1到之前c=1a=1之后b=1,因为线程 2 无法观察到它,因为线程 1 执行的同步块看起来像线程 2 的原子操作。

另一方面,不使用相同锁的线程将能够观察到这些重新排序。

于 2013-09-21T06:32:57.910 回答
4

只要尊重发生之前,运行时可能会重新排序操作。规范写道

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

更具体地说,如果两个动作共享一个happens-before 关系,那么对于它们不共享happens-before 关系的任何代码,它们不一定必须以该顺序发生。例如,一个线程中的写入与另一个线程中的读取处于数据竞争中,这些读取可能会出现乱序。

特别是,如果一个线程这样做:

synchronized (lock) {
    x = 1;
    y = 1;
}

另一个是:

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

这些线程之间没有happens-before关系,其他线程可能观察y到之前已经被赋值过x,并打印0。

于 2013-09-21T09:53:28.527 回答