发生在不同的监视器之前

Happens-before with different monitors

在这个网站的某个地方(我不记得这个问题了)有人声称 happens-before relashionship 对不同的监视器和不同的可变变量成立,即

// Thread T1
synchronized(O1)
{
}

// Thread T2
synchronized(O2)
{
}

如果线程 T2 在线程 T1 离开 synchronized(O1){} 块后进入 synchronized(O2){} 块,则 T2 将看到 T1 所做的所有更改。解释是,当线程离开同步块(或写入 volatile 变量)时,它会将其缓存刷新到内存中。当线程进入同步块(或读取 volatile 变量)时,它会丢弃它的缓存并从内存中读取。是真的吗?

在 volatile 变量的关系为真之前发生。如果在保持关系之前发生同步块,以防您锁定同一对象。在您的情况下,synchronized 用于不同的对象,这意味着 2 个不同的线程可以进入两个不同的 synchronized 块。在这种情况下,java 内存模型不能保证发生在关系之前。

使用 synchronizedvolatile 的记忆效应就是记忆效应。观察到内存为 "flushed from cache" 的事实与 synchronizedvolatile 没有直接关系。 Happens-before 关系只有在操作作为 JLS 17.4.5 中指定的操作的结果或作为 happens-before[ 中的结果而发生时才为真=27=] 链(即搭载)与这些变量。如果对不同的变量执行两个 synchronizedvolatile 操作,则存在 NO happens-before 关系。

记忆效应是 happens-before 排序的结果,但 happens-before 永远不会是记忆效应的结果。

不同监视器之间没有同步关系。

在获取锁和后续执行之间存在先行关系,在执行和后续锁释放之间存在先行关系。

如果一个线程在另一个线程离开同步块后进入同步块,则在较早线程上执行同步块与在后一个线程上执行同步块之间可能存在先行关系.

您只能确定同步块是否或何时不会 运行 与同步对象关系并发。没有这种关系,就会出现数据竞争。

如果您显示的代码是线程之间的全部,则不存在同步关系,因此不存在发生前关系。答案的其余部分显示了代码比问题中提供的代码多的情况。

建立同步关系的一件事是加入一个线程,例如等待前一个线程完成。另一种是启动一个线程,比如前一个线程结束后启动后一个线程。

// In a thread
t1.start();
t1.join();
t2.start();

// Or in thread 1
synchronized (O1) {
    // actions in thread 1
}
t2.start();

// Or in thread 2
t1.join();
synchronized (O2) {
    // actions in thread 2
}

或者如果后一个线程正在获取前一个线程拥有的外部锁(最终释放):

// in thread 1
synchronized (outerLock) {
    synchronized (O1) {
        // actions in thread 1
    }
}

// in thread 2
synchronized (outerLock) {
    synchronized (O2) {
        // actions in thread 2
    }
}

或者如果后一个线程正在等待前一个线程可验证地通知的锁:

boolean done;

// in thread 1
synchronized (O1) {
    // actions in thread 1
}
synchronized (commonLock) {
    done = true;
    commonLock.notify();
}

// in thread 2
synchronized (commonLock) {
    while (!done) {
        commonLock.wait();
    }
}
synchronized (O2) {
    // actions in thread 2
}

或者如果后一个线程读取前一个线程写入的volatile变量:

volatile boolean done;

// in thread 1
synchronized (O1) {
    // actions in thread 1
}
done = true;

// in thread 2
while (!done) {
    Thread.yield();
}
synchronized (O2) {
    // actions in thread 2
}

在这种特殊情况下,线程 2 只有在线程 1 写入 done 之后才确定它已经读取 done 如果它观察到变化。

实际上,所有这些情况都具有synchronizes-with关系,即happens-before关系。同步操作是保证总顺序的操作,因此并非所有先行关系都发生在线程之间。

Java Language Specification 8th Edition §17中,您可以阅读有关同步操作的信息:

A synchronization order is a total order over all of the synchronization actions of an execution.

并且您可以阅读以下关于 happens-before 关系的内容:

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.

这允许在单个线程中,非易失性读取在最新获取之后重新排序,非易失性写入在下一次发布之前重新排序。

所以,如果你从中得到一些东西:

  • synchronizes-with关系是关于多线程的

  • 并非所有事前发生关系都与多线程有关