0

在这个网站上的某个地方(我不记得这个问题)有人声称,对于不同的监视器和不同的 volatile 变量,发生之前发生的 relashionship,即

// Thread T1
synchronized(O1)
{
}

// Thread T2
synchronized(O2)
{
}

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

4

3 回答 3

2

由于使用synchronizedvolatile只是这样的记忆效应 - 记忆效应。观察到内存“从缓存中刷新”的事实与synchronizedor没有直接关系volatile仅当由于JLS 17.4.5中指定的操作或由于与这些变量处于先发生链(即捎带)中而发生操作时,才发生关系才成立。如果您对不同的变量执行两个或操作,则没有发生之前的关系。synchronizedvolatile

记忆效应是发生在排序之前的结果发生之前永远不会是记忆效应的结果。

于 2017-03-07T15:16:16.490 回答
0

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

于 2017-03-07T10:19:36.757 回答
0

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

在获取锁和随后的执行之间存在发生前的关系,在执行和随后的锁释放之间存在发生前的关系。

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

您只能确定同步块是否或何时不与 synchronizes-with 关系同时运行。没有这种关系,你就会有数据竞赛。

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

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

// 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 仅done在线程 1 写入后才能确定它是否已读取,done如果它观察到更改。

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

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

同步顺序是执行的所有同步操作的总顺序。

您可以阅读以下关于发生前关系的内容:

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

这允许在单个线程中,将非易失性读取重新排序至最近一次获取之后,并且将非易失性写入重新排序至下一个版本之前。

所以,如果你从中取出一些东西:

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

  • 并非所有发生之前的关系都与多个线程有关

于 2017-03-11T22:52:27.297 回答