4

我对 Java 的发生之前和同步有一点分歧。

想象以下场景:

主线程

MyObject o = new MyObject();    // (0)
synchronized (sharedMonitor) {
    // (1) add the object to a shared collection
}
// (2) spawn other threads

其他主题

MyObject o;
synchronized (sharedMonitor) {
    // (3) retrieve the previously added object
}
// (4) actions to modify the object

请注意, 的实例变量MyObject既不是volatile,也不是final。的方法MyObject不使用同步。

我的理解是:

  • 1 发生在 3之前,因为在同一个监视器上存在同步,并且其他线程仅在2产生,它在1之后执行。

  • 4上的操作不能保证稍后对主线程可见,除非所有线程都有进一步的同步,并且主线程在这些操作之后以某种方式同步。

问:是否可以保证0处的操作可见、发生之前、3上的并发访问,或者我必须将变量声明为volatile


现在考虑以下场景:

主线程

MyObject o = new MyObject();    // (0)
synchronized (sharedMonitor) {
    // (1) add the object to a shared collection
}
// (2) spawn other threads, and wait for their termination
// (5) access the data stored in my object.

其他主题

MyObject o;
synchronized (sharedMonitor) {
    // (3) retrieve the previously added object
}
o.lock();    // using ReentrantLock
try {
    // (4) actions to modify the object
} finally { o.unlock(); }

我的理解是:

  • 1 发生在 3之前,就像以前一样。

  • 4上的操作在其他线程之间是可见的,ReentrantLock因为MyObject.

  • 4上的操作在逻辑上发生在3之后,但是由于在不同的监视器上同步,从34没有发生之前的关系。

  • 即使在 4 之后有同步,上述观点仍然sharedMonitor适用。unlock

  • 4上的操作不会在5上的访问之前发生,即使主线程等待其他任务终止。这是由于5上的访问没有与 同步,所以主线程可能仍然看到过时的数据。o.lock()

问:我的理解正确吗?

4

1 回答 1

9

问:是否可以保证 0 处的操作是可见的、之前发生的、3 上的并发访问,或者我必须将变量声明为 volatile 吗?

是的,有保证。您不需要synchronized主线程中有块,因为在启动线程时存在先发生关系。从 JLS 17.4.5 开始:“在线程上调用 start() - 在已启动线程中的任何操作之前发生。”

这也意味着如果你将你的传递o给线程构造函数,你也不需要synchronized(3) 周围的块。

(4) 上的操作在逻辑上发生在 (3) 之后,但由于在不同的监视器上同步,从 (3) 到 (4) 没有发生之前的关系。

是和不是。逻辑顺序意味着在同一个线程中肯定存在先发生关系,即使它是不同的监视器。即使它们正在处理不同的监视器,编译器也无法重新排序 3 过去的 4。volatile对字段的访问也是如此。

对于多个线程,由于 (3) 仅读取对象,因此不存在竞争条件。但是,如果 (3) 正在对对象进行修改(而不是仅仅读取它),那么在另一个线程中,这些修改可能不会在 (4) 中看到。正如您引用和@StephenC 重申的那样,JLS 表示,happens-before 关系仅在同一个监视器上得到保证。JLS 17.4.5:“监视器上的解锁发生在该监视器上的每个后续锁定之前。”

即使在 (4) 解锁后 sharedMonitor 上存在同步,上述观点仍然适用。

看上面。

(4) 上的操作不会在 (5) 上的访问之前发生,即使主线程等待其他任务终止

不。一旦主线程调用thread.join()并且它返回而不会被中断,那么主线程将与它加入的线程的内存完全同步。在被连接的线程和执行连接的线程之间存在发生前的关系。JLS 17.4.5:“线程中的所有操作都发生在任何其他线程从该线程上的 join() 成功返回之前。”

于 2013-06-19T12:50:20.630 回答