Java 原子参考的保证
Java 原子参考的保证,java,multithreading,concurrency,java.util.concurrent,Java,Multithreading,Concurrency,Java.util.concurrent,原子引用的语义是什么? 如果我这样做: AtomicReference ref=新的AtomicReference() 然后我会: public void someMethod(){ //do something ref.set(loadNewData()); } private final Sempahore updatePermit = new Sempahore(1); private CustomObject loadNewData(){ C
原子引用的语义是什么?
如果我这样做:
AtomicReference ref=新的AtomicReference()代码>
然后我会:
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
将返回most最新版本的CustomObject
?
这与assylias的回答有关:是。
AtomicReference
保证发布
但是,不能保证其他线程不会在毫秒后设置它
请注意,如果您没有调用compareAndSet()
,则原子引用
并不比volatile
字段好。读取:
get
具有读取volatile
变量的记忆效果
set
具有写入(分配)一个volatile
变量的记忆效果
因此,保证是:get()
始终返回传递给set()
的最后一个值。同时,您做什么、使用什么类型的锁等都无关紧要。修改volatile
变量(您有效地这么做)保证所有其他读取该值的线程都可以看到。实际上,您的代码有一个争用条件,可能会导致新数据丢失:
第一个调用者进入loadNewData并开始加载新数据(具有信号量)
第二个调用方无法获取信号量并在获取时等待
第一个调用方完成加载并释放信号量(但尚未返回新数据)
第二个调用方获取信号量,调用ref.get()
并获取旧数据
第一个调用者返回传递给ref.set()的新数据
第二个调用者返回旧数据,在传递到ref.set()时覆盖新数据。
由于someMethod()
总是在加载新数据,而您总是希望调用方在加载新数据时等待,所以所有这些额外的东西都是无用的。只需在整个块周围使用一个简单的同步块(或锁),并放弃原子引用。根据链接的帖子,似乎你只想做一次,所以使用初始化标志
private boolean _initialized;
public synchronized void loadLatestData() {
if(!_initialized) {
// load latest data ...
_initilized = true;
}
}
原子变量(包括AtomicReference
)与volatile
字段具有相同的属性,因为它们在线程之间建立了“发生在之前”的关系。如果一个线程A向一个易失性字段(或原子变量)写入一个值,而另一个线程B从同一个变量中读取,那么您不一定能保证B是否会看到A的更改,但您可以保证这一点
- 如果B看到A写入的值
- 然后B还将看到A在写入volatile字段之前写入其他字段(volatile和non-volatile)的结果
如果线程A
执行updatePermit.release()代码>是否保证线程B
能够“查看”从线程a
返回的值?也就是说,ref
会在B
释放信号量后尝试执行get
之前更新吗?但是我不需要compareAndSet
@Jim:那么你最好改用volatile
。如果线程A
会updatePermit.release()代码>是否保证线程B
能够“查看”从线程a
返回的值?也就是说,ref
是否会在B
释放信号量后尝试执行get
之前更新?@Jim:信号量与此无关,请阅读volatile
变量保证。Somephores和同步只与普通变量相关。那么什么时候人们更喜欢AtomicReference
而不是volatile
?@Jim:如果你只使用get()
和set()
,除了可读性之外,没有什么真正的好处atomic*
在使用CAS操作和其他高性能非阻塞方法(如incrementAndGet()
)时非常有用。解决方法是将ref.set
移动到loadNewData
内,然后再释放@IanRoberts-是,或者干脆抛开那些废话,使用一个简单的锁。@jtahlborn我不认为一个简单的锁可以解决链接帖子中详细描述的原始问题。不过我同意比赛条件。如果两个线程T1和T2调用loadNewData
,T1应该加载数据,T2应该等待T1加载数据,并在T1完成加载后退出该方法。@assylias-我从未实际查看过链接的帖子。然而,一个简单的布尔“initialized”标志将解决“仅执行此操作一次”的问题。将更新。@jtahlborn这实际上不是“只做一次”的问题。见我上面的评论(链接帖子中的原始问题不清楚)。