gpt4 book ai didi

java - 为什么需要在 CopyOnWriteArrayList 中调用 setArray() 方法

转载 作者:搜寻专家 更新时间:2023-10-30 20:01:05 25 4
gpt4 key购买 nike

CopyOnWriteArrayList.java , 在方法 set(int index, E element)以下:

public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
Object oldValue = elements[index];

if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);----? Why this call required?
}
return (E)oldValue;
} finally {
lock.unlock();
}
}
为什么要调用 setArray是必须的?我无法理解该方法调用上面写的注释。是不是因为我们没有使用同步块(synchronized block),我们必须手动刷新我们使用的所有变量?在上面的方法中,他们使用了可重入锁。如果他们使用了synchronized 语句,他们是否还需要调用 setArray方法?。我想不是。
问题2:如果我们以else结束,就说明我们没有修改elements数组,那为什么还要flush变量数组的值呢?

最佳答案

此代码使用深度 Java 内存模型 voodoo,因为它混合了锁和 volatile 。

不过,这段代码中的锁用法很容易省却。锁定在使用相同锁的线程之间提供内存排序。具体来说,此方法末尾的解锁为获取相同锁的其他线程提供了发生之前的语义。但是,通过此类的其他代码路径根本不使用此锁。因此,锁的内存模型含义与那些代码路径无关。

那些其他代码路径确实使用 volatile 读取和写入,特别是对 array field 。 getArray方法对该字段进行 volatile 读取,setArray方法方法对该字段进行 volatile 写入。

此代码调用 setArray 的原因即使显然没有必要,它也为该方法建立了一个不变量总是 对该数组执行 volatile 写入。这与从该数组执行 volatile 读取的其他线程建立了happens-before 语义。这很重要,因为 volatile 写-读语义适用于 volatile 字段本身以外的读和写。具体来说,在 volatile 写入发生之前写入其他(非 volatile )字段,然后在对同一 volatile 变量进行 volatile 读取之后从其他字段读取。见JMM FAQ为解释。

下面是一个例子:

// initial conditions
int nonVolatileField = 0;
CopyOnWriteArrayList<String> list = /* a single String */

// Thread 1
nonVolatileField = 1; // (1)
list.set(0, "x"); // (2)

// Thread 2
String s = list.get(0); // (3)
if (s == "x") {
int localVar = nonVolatileField; // (4)
}

假设第 (3) 行获取第 (2) 行设置的值,即内部字符串 "x" . (为了这个例子,我们使用内部字符串的身份语义。)假设这是真的,那么内存模型保证在第 (4) 行读取的值将是第 (1) 行设置的 1。这是因为 (2) 处的 volatile 写入以及每次较早的写入都发生在第 (3) 行处的 volatile 读取以及每次后续读取之前。

现在,假设初始条件是列表已经包含一个元素,即内部字符串 "x" .并进一步假设 set()方法的 else条款没有使 setArray称呼。现在,根据列表的初始内容, list.set()在第 (2) 行调用可能会也可能不会执行 volatile 写入,因此第 (4) 行的读取可能有也可能没有任何可见性保证!

显然,您不希望这些内存可见性保证依赖于列表的当前内容。在所有情况下建立保证, set()在所有情况下都需要进行 volatile 写入,这就是它调用 setArray() 的原因。即使它自己没有做任何写作。

关于java - 为什么需要在 CopyOnWriteArrayList 中调用 setArray() 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28772539/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com