2

的语义是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?
这与 assylias 对post的回答有关:

4

4 回答 4

3

是的。
AtomicReference保证出版。

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

请注意,如果您不调用compareAndSet(),则 anAtomicReference并不比volatile字段好。

于 2012-11-13T14:29:15.397 回答
3

阅读JavaDoc 的java.util.concurrent.atomic

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

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

所以保证是:get() 总是返回传递给的最后一个值set()。在此期间你做了什么,你使用什么样的锁等等都没有关系。修改volatile变量(你实际上是这样做的)保证对读取该值的所有其他线程可见。

于 2012-11-13T14:30:51.057 回答
3

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

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

由于someMethod()总是加载新数据,并且您总是希望调用者加载新数据时等待,所有这些额外的东西都是无用的。只需在整个块周围使用一个简单的同步块(或锁)并放弃原子引用。根据链接的帖子,您似乎只想这样做一次,因此请使用初始化标志。

private boolean _initialized;

public synchronized void loadLatestData() {
  if(!_initialized) {
      // load latest data ...
      _initilized = true;
  }
}
于 2012-11-13T14:32:42.883 回答
1

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

  • IF B看到A写入的值
  • 那么 B还将看到A在写入 volatile 字段之前对其他字段(易失性和非易失性)所做的任何写入的结果。
于 2012-11-13T14:43:58.623 回答