Java:如何动态保证"数据"的可见性在这段代码中?

时间:2015-11-11 00:00:48

标签: multithreading synchronization visibility volatile happens-before

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。
  3. 我的困惑是

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

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

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

    5. 谢谢!

1 个答案:

答案 0 :(得分:2)

  

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

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

  

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

那是对的。

  

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

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

  

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

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

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

Java volatile操作被定义为使得看到更改为1的线程保证在线程看到更改之前看到所做的所有其他内存更改。在其他语言(例如C或C ++)中则不然。这可能会使Java的易失性在某些平台上变得更加昂贵,但幸运的是并不是在典型的平台上。

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