1
Class Future
{
    private volatile boolean ready;
    private Object data;
    public Object get()
    {
        if(!ready) return null;
        return data;
    }

    public synchronized void setOnce(Object o)
    {
        if(ready) throw...;
        data = o;
        ready = true;
    }
}

它说“如果一个线程读取数据,则从写入到读取存在一个先发生边缘,以保证数据的可见性”

从我的学习中我知道:

  1. volatile 确保每次读/写都在内存中,而不仅仅是在缓存或寄存器中;
  2. volatile 确保重新排序:也就是说,在 setOnce() 方法中 data = o 只能安排在 if(ready) throw... 之后,并且在 ready = true 之前;这保证如果在 get() ready = true 中,数据必须为 o。

我的困惑是

  1. 是否有可能当线程 1 在 setOnce() 中时,到达 data = o 之后的点;准备好之前=真;同时线程2运行get(),read ready为false,返回null。并且 thead 1 继续准备好 = true。在这种情况下,线程 2 没有看到新的“数据”,即使数据已在线程 1 中分配了新值。

  2. get() 不同步,这意味着同步锁无法保护 setOnce(),因为线程 1 调用 get() 不需要获取锁来访问变量就绪数据。所以线程不能保证看到最新的数据值。我的意思是锁只保证同步块之间的可见性。即使一个线程正在运行同步块 setOnce(),另一个线程仍然可以进入 get() 并在不阻塞的情况下访问就绪和数据,并且可能会看到这些变量的旧值。

  3. 在 get() 中,如果 ready = true,则数据必须为 o?我的意思是这个线程可以保证看到数据的可见性?我认为 data 不是 volatile 也不是 get() 同步的。这个线程是否可以看到缓存中的旧值?

谢谢!

4

1 回答 1

3

volatile 确保每次读/写都在内存中,而不仅仅是在缓存或寄存器中;

没有。它只是确保它对其他线程可见。在现代硬件上,这不需要访问内存。(这是一件好事,主内存很慢。)

volatile 确保重新排序:也就是说,在 setOnce() 方法中 data = o 只能安排在 if(ready) throw... 之后,并且在 ready = true 之前;这保证如果在 get() ready = true 中,数据必须为 o。

这是正确的。

是否有可能当线程 1 在 setOnce() 中时,到达 data = o 之后的点;准备好之前=真;同时线程2运行get(),read ready为false,返回null。并且 thead 1 继续准备好 = true。在这种情况下,线程 2 没有看到新的“数据”,即使数据已在线程 1 中分配了新值。

是的,但如果这是一个问题,那么你不应该使用这样的代码。据推测,此代码的 API 将get保证在返回后调用时看到结果setOnce。显然,您不能保证get在我们完成制作之前会看到结果。

get() 不同步,这意味着同步锁无法保护 setOnce(),因为线程 1 调用 get() 不需要获取锁来访问变量就绪数据。所以线程不能保证看到最新的数据值。我的意思是锁只保证同步块之间的可见性。即使一个线程正在运行同步块 setOnce(),另一个线程仍然可以进入 get() 并在没有阻塞的情况下访问就绪和数据,并且可能会看到这些变量的旧值。

不。如果这是真的,同步几乎是不可能使用的。例如,一种常见的模式是创建一个对象,然后获取集合上的锁并将对象添加到集合中。如果获取集合上的锁不能保证创建对象所涉及的写入是可见的,这将不起作用。

在 get() 中,如果 ready = true,则数据必须为 o?我的意思是这个线程可以保证看到数据的可见性?我认为 data 不是 volatile 也不是 get() 同步的。这个线程是否可以看到缓存中的旧值?

Java 的volatile操作是这样定义的,以确保看到对一个更改的线程可以看到所有其他内存更改,该线程在线程看到的更改之前进行了该更改。这在其他语言(例如 C 或 C++)中是不正确的。这可能会使 Java 的 volatile 在某些平台上变得更加昂贵,但幸运的是在典型平台上不会。

另外,请不要谈论“在缓存中”。这与缓存无关。这是一个常见的误解。它与可见性有关,而不是缓存。大多数缓存提供对缓存的完全可见性(将“MESI 协议”打入您最喜欢的搜索引擎以了解更多信息)并且不需要任何特殊的东西来确保可见性。

于 2015-11-11T01:33:53.720 回答