Java的happens-before和同步

2024-02-16

我对 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, nor final。 的方法MyObject不要使用同步。

我的理解是:

  • 1 发生在之前 3,因为同一监视器上有同步,并且其他线程仅在2,这是在之后执行的1.

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

Q:是否有任何行动保证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,但是没有发生在之前关系来自3 to 4,作为在不同监视器上同步的结果。

  • 即使存在同步,上述观点仍然成立sharedMonitor之后unlock of 4.

  • 行动于4 do not 发生在之前的访问权限5,即使主线程等待其他任务终止。这是由于访问5不与同步o.lock(),因此主线程可能仍然会看到过时的数据。

Q:我的理解正确吗?


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

是的,有保证。你做not需要有synchronized阻塞在主线程中,因为线程启动时存在happens-before关系。从 JLS 17.4.5 开始:“对线程上的 start() 的调用发生在已启动线程中的任何操作之前。”

这也意味着,如果您通过了o进入线程构造函数,你不需要synchronized也可以围绕 (3) 进行阻挡。

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

是和不是。逻辑顺序意味着在同一线程尽管是不同的监视器,但肯定存在先发生的关系。即使它们处理不同的监视器,编译器也无法对 3 和 4 进行重新排序。访问 a 时也是如此volatile field.

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

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

往上看。

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java的happens-before和同步 的相关文章

随机推荐