Java:volatile如何保证这段代码中"data"的可见性?

Java: how volatile guarantee visibility of "data" in this piece of code?

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;
    }
}

它说"if a thread reads data, there is a happens-before edge from write to read of that guarantees visibility of data"

我从我的学习中知道:

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

我的困惑是

  1. 有没有可能线程1在setOnce()时,到达data = o之后的点;准备好之前=真;同时,线程2遇到get(),read ready为false,returnnull。而 thead 1 继续 ready = true。 在这种情况下,线程 2 没有看到新的 "data",即使数据已在线程 1 中分配了新值。

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

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

谢谢!

volatile ensures that every read/write will be in the memory instead of only in cache or registers;

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

volatile ensures reorder: that is, in setOnce() method data = o can only be scheduled after if(ready) throw..., and before ready = true; this guarantee that if in get() ready = true, data must be o.

没错。

is it possible that when thread 1 is in setOnce(), reaches the point that after data = o; before ready = true; At the same time, thread 2 runs into get(), read ready is false, and return null. And thead 1 continues ready = true. In this scenario, Thread 2 didn't see the new "data" even though data has been assigned new value in thread 1.

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

get() isn't synchronized, that means the synchronized lock cannot protect setOnce() since thread 1 calls get() that needn't acquire the lock to access variable ready, data. So thread are not guaranteed to see the latest value of data. By this, I mean lock only guarantee the visibility between synchronized blocks. Even though one thread is running synchronized block setOnce(), another thread is still can go into get() and access ready and data without blocking and may see the old value of these variables.

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

in get(), if ready = true, data must be o? I mean this thread is guaranteed to see the visibility of data? I think data is not a volatile nor the get() synchronized. Is this thread may see the old value in the cache?

Java 的 volatile 操作被定义为保证看到一个线程发生变化的线程看到所有其他内存变化 做出该变化的线程在它做出变化之前线锯。这在其他语言(如 C 或 C++)中并非如此。这可能会使 Java 的挥发物在某些平台上更加昂贵,但幸运的是在典型平台上不会。

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