我对 AtomicReference 如何保证原子性有点困惑getAndUpdate。考虑以下示例
示例1
AtomicReference<Set<String>> set = new AtomicReference<>(new HashSet<>());
set.getAndUpdate(current -> {
Set<String> updated = new HashSet<>();
updated.add("test");
return updated;
});
Run Code Online (Sandbox Code Playgroud)
示例2
AtomicReference<Set<String>> set = new AtomicReference<>(new HashSet<>());
set.getAndUpdate(current -> {
current.add("test");
return current;
});
Run Code Online (Sandbox Code Playgroud)
在示例 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);
Run Code Online (Sandbox Code Playgroud)
在示例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");
}
Run Code Online (Sandbox Code Playgroud)
示例 1 将保证原子性,因为修改发生在新集合上。但它如何与 [AtomicReference.set()] 不同呢?
您的示例 1 使用AtomicReference.getAndUpdate它返回先前的值,并允许您根据先前的值生成新值。如果您不需要知道之前的值是多少,则可以直接调用AtomicReference.set.
| 归档时间: |
|
| 查看次数: |
1312 次 |
| 最近记录: |