3

我最近阅读了http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html,它清楚地描述了 Java 内存模型的许多内在特性。一个特别的摘录引起了我的注意,即:

The rule for a monitorexit (i.e., releasing synchronization) is that 
actions before the monitorexit must be performed before the monitor is released.

对我来说似乎很明显,但是在阅读了http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html和发生之前的定义后,我能找到的关于显示器解锁的所有信息都是当一个线程解锁发生的监视器- 在另一个线程再次锁定它之前(这也很有意义)。有人能解释一下 JLS 如何解释同步块中的所有操作都必须在解锁操作之前发生的明显条件吗?

进一步的评论:

基于几个回复,我想对你们一直在说的话写下进一步的评论:

  1. 在单线程中重新编码

我引用的来源中的几个“真相”:

a = new A()

如果new A()涉及一百个操作,然后将堆上的地址分配给a,编译器可以简单地重新排序这些操作以分配堆地址a,然后按照通常的初始化(双重检查锁定的问题)

synchronized{
    a = 5;
}
print a;

可以改为

synchronized{
    a = 5;
    print a;
}

所以我们monitorexit用打印语句重新排序(根据 JLS 也有效)

现在,我提到的简单案例:

x = 1;
y = 2;
c = x + y;
print c;

我认为没有理由阻止编译器先分配 x 或先分配 y。完全没有什么可以阻止它,因为无论是先分配 x 还是先分配 y,最终输出都不会改变。所以重新排序是完全可能的。

  1. 监视器.解锁

基于 print 语句被“拉入”同步块的示例,让我们尝试扭转这一点,即 startwing

synchronized{
    a = 5;
    print a;
}

我可以期望编译器这样做:

synchronized{
    a = 5;
}
 print a;

在单线程世界中似乎完全合理,但是绝对是无效的,并且针对 JLS(根据引用的来源)。如果我在 JLS 中找不到任何关于此的内容,为什么会出现这种情况?显然,关于“程序顺序”的动机现在已经无关紧要了,因为编译器可以进行重新排序,例如将语句“拉入”到同步块中。

4

2 回答 2

2

它不仅仅是在synchronized块内执行的所有操作,它还指的是该线程在monitorexit.

有人可以解释 JLS 如何解释同步块中的所有操作都必须在解锁操作之前发生的明显条件吗?

对于一个特定的线程(并且只有一个线程),所有操作都不管synchronized维护程序顺序,所以看起来好像所有的读取和写入都是按顺序发生的(在单线程情况下,我们不需要先发生顺序)。

发生之前的关系考虑了多个线程,即在 monitorexit 之前发生在一个线程中的所有操作在连续的 之后对所有线程都是可见的monitorenter

编辑以解决您的更新。

编译器必须遵循特定规则才能重新排序。在这种情况下的具体情况在此处的Can Reorder网格中进行了演示

特别有用的条目是

  • 第一个动作:正常加载(加载 a;打印 a)
  • 第二个动作:监控退出

这里的值是No意味着编译器不能重新排序两个动作,其中第一个是正常加载,第二个是monitorexit这样,在你的情况下,这个重新排序会违反 JLS。

有一个规则称为 roach-motel ordering,即读/写可以重新排序到同步块中,但不能退出。

于 2014-02-18T13:09:25.710 回答
1

也许你错过了这个(§17.4.5):

如果 x 和 y 是同一线程的操作,并且 x 在程序顺序中位于 y 之前,则为 hb(x, y)。

结合你已经知道的happens-before,应该清楚这意味着解锁操作之前的所有操作对另一个线程都是可见的。

关于您对问题的补充,如果您这样写:

synchronized {
    a = 5;
    b = 3;
}

并且编译器发出这个:

synchronized{
    a = 5;
}
b = 3;

那么我上面引用的规定就被违反了: now b = 3不会在lock-release之前发生。这就是为什么它是非法的。(请注意,您的示例print a并不具有指导意义,因为它仅涉及阅读+副作用,而简单的变量不容易描述。)

于 2014-02-18T13:14:26.127 回答