保证AtomicReference

时间:2012-11-13 14:24:01

标签: java multithreading concurrency java.util.concurrent

AtomicReference的语义是什么? 如果我这样做:

AtomicReference<CustomObject> ref = new AtomicReference<CustomObject>();

然后我做:

public void someMethod(){  
 //do something  

 ref.set(loadNewData());  

}  

private final Sempahore updatePermit = new Sempahore(1);  

private CustomObject loadNewData(){  
     CustomObject obj = null;  
     if (updatePermit.tryAcquire()) {  
        obj = ...; //do the loading and create Object  
        updatePermit.release();  
    } else {  
        //update already running, wait  
        updatePermit.acquire();  
        //release the permit immediately  
        updatePermit.release();  
        obj = ref.get(); //????
    }  
    return obj;    
}    

保证obj = ref.get(); //????get会返回 最新版CustomObject吗?
这与post的assylias答案有关:

4 个答案:

答案 0 :(得分:3)

是。
AtomicReference保证发布。

但是,不能保证不同的线程不会在一个毫秒后设置它。

请注意,如果您没有致电compareAndSet(),则AtomicReference并不比volatile字段更好。

答案 1 :(得分:3)

阅读JavaDoc of java.util.concurrent.atomic

  
      
  • get具有读取volatile变量的记忆效应。

  •   
  • set具有编写(分配)volatile变量的记忆效应。

  •   

所以保证是:get() 总是返回传递给set()的最后一个值。在此期间你做了什么并不重要,你使用什么样的锁等等。修改volatile变量(你有效地做)确保被读取该值的所有其他线程都可见。

答案 2 :(得分:2)

实际上,您的代码存在竞争条件,可能导致新数据丢失:

  1. 第一个调用者进入loadNewData并开始加载新数据(有信号量)
  2. 第二个来电者无法获取信号量并等待获取
  3. 第一个调用者完成加载并释放信号量(但尚未返回新数据)
  4. 第二个来电者获取信号量,拨打ref.get()并获取数据
  5. 第一个调用者返回传递给ref.set()
  6. 的新数据
  7. 第二个调用方返回数据,在传递给ref.set()
  8. 时会覆盖新数据

    由于someMethod() 总是加载新数据,并且总是希望调用者在加载新数据时等待,所有这些额外的东西都是无用的。只需在整个块周围使用一个简单的同步块(或Lock)并抛弃原子参考。根据链接的帖子,看起来你只想做一次,所以使用初始化标志。

    private boolean _initialized;
    
    public synchronized void loadLatestData() {
      if(!_initialized) {
          // load latest data ...
          _initilized = true;
      }
    }
    

答案 3 :(得分:1)

原子变量(包括AtomicReference)与volatile字段具有相同的属性,因为它们在线程之间建立了先发生关系。如果一个线程 A 将值写入易失性字段(或原子变量)而另一个线程 B 从同一个变量读取,那么您无法保证是否 B 将会或不会看到 A 的更改,但您可以保证

  • IF B 看到 A
  • 写的值
  • 然后 B 还会看到其他字段(易失性和非易失性)的任何写入结果 A < / em>在编写volatile字段之前做过。