【发布时间】:2016-02-12 00:53:54
【问题描述】:
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;
}
}
它说“如果一个线程读取数据,则从写入到读取存在一个先发生边缘,以保证数据的可见性”
我从我的学习中知道:
- 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。 在这种情况下,即使数据已在线程 1 中分配了新值,线程 2 也没有看到新的“数据”。
get() 未同步,这意味着同步锁无法保护 setOnce(),因为线程 1 调用 get() 无需获取锁即可访问变量就绪数据。所以线程不能保证看到最新的数据值。我的意思是锁只保证同步块之间的可见性。即使一个线程正在运行同步块 setOnce(),另一个线程仍然可以进入 get() 并在不阻塞的情况下访问就绪和数据,并且可能会看到这些变量的旧值。
在 get() 中,如果 ready = true,数据必须为 o?我的意思是这个线程可以保证看到数据的可见性?我认为 data 不是 volatile 也不是 get() 同步的。这个线程是否可以看到缓存中的旧值?
谢谢!
【问题讨论】:
-
这是什么语言?爪哇?
-
另外,您的
1大多是错误的。volatile关键字与内存可见性有关,而不是缓存。缓存由缓存一致性硬件处理。这显然是一个没有人会使用的糟糕设计——内存太慢了,无法以这种方式使用。 -
@DavidSchwartz 在 Java 中可以将变量存储在缓存中。 L1 和 L2 缓存对于不同的线程是不可见的,使用 volatile 将值存储在主内存或 L3 缓存中(主内存和 L3 缓存在线程之间共享)。 More info
-
@VelkoGeorgiev 这完全是错误的。这不是缓存的工作方式。这是一个普遍的神话,但它只是一个神话。
volatile关键字与这些缓存没有任何关系。对volatile的访问可以完全保留在 L1 缓存中,没有任何问题。 (遗憾的是,您链接到的文章重复了这个神话。) -
@VelkoGeorgiev 我在文章上做了一些 cmets。当一个人如此彻底地误解了一个重要问题并试图将其教给其他人时,这令人愤怒。
标签: multithreading synchronization visibility volatile happens-before