对于AtomicReference getAndUpdate如何保证原子性,我有点困惑。请考虑以下示例
例1
AtomicReference<Set<String>> set = new AtomicReference<>(new HashSet<>());
set.getAndUpdate(current -> {
Set<String> updated = new HashSet<>();
updated.add("test");
return updated;
});例2
AtomicReference<Set<String>> set = new AtomicReference<>(new HashSet<>());
set.getAndUpdate(current -> {
current.add("test");
return current;
});在示例2中,将在getAndUpdate的回调中修改集合。如果多个线程试图同时访问此函数,那么它们会看到修改后的状态还是getAndUpdate在将原始集传递给回调时通过克隆原始集来防止这种情况,这样在一个线程中就不会看到其他线程中的修改了吗?如果示例2不能保证原子性,为什么getAndUpdate允许我们编写这段代码?
示例1将保证原子性,因为修改发生在新集合上。但是它是怎么从下面来的呢?
AtomicReference<Set<String>> set = new AtomicReference<>(new HashSet<>());
Set<String> updated = new HashSet<>();
updated.add("test");
set.set(updated);发布于 2021-08-27 21:03:49
在示例2中,集合将在getAndUpdate的回调中进行修改。如果多个线程试图同时访问此函数,那么它们会看到修改后的状态还是getAndUpdate在将原始集传递给回调时通过克隆原始集来防止这种情况,这样在一个线程中就不会看到其他线程中的修改了吗?
没有什么可以阻止多个线程看到对集合的修改。只有对引用的更改是原子性的,而不是对引用所引用的任何内容的更改。
如果示例2不能保证原子性,为什么getAndUpdate允许我们编写这段代码?
因为它阻止不了你。编译器不够聪明,不知道示例2是坏代码。
为了最小化意外/不安全地修改共享状态的风险,确保存储在原子引用中的东西是不可变的,或者至少是不可修改的:
AtomicReference<Set<String>> set = new AtomicReference<>(Collections.emptySet());
set.getAndUpdate(current -> {
Set<String> updated = new HashSet<>();
updated.add("test");
return Collections.unmodifiableSet(updated);
});
// or if you're on Java 9+
set.getAndUpdate(current -> {
return Set.of("test");
}示例1将保证原子性,因为修改发生在新集合上。但是它是如何脱离AtomicReference.set()的呢?
示例1使用AtomicReference.getAndUpdate,它返回前一个值,并允许您根据前面的值生成新值。如果不需要知道前一个值是什么,只需调用AtomicReference.set。
https://stackoverflow.com/questions/68959187
复制相似问题