当在Reentrant
中添加元素时,为什么需要根据CopyOnWriteArrayList
中的以下代码获取List
锁。我们正在创建原始数组的副本,然后对其进行修改。如果不首先获得lock
,我们会有什么副作用?
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
答案 0 :(得分:2)
当您尝试在多线程上下文中对全局变量进行任何操作时,希望它既是 atomic ,又要确保内存可见性对其他线程的作用需要锁定该操作。
此处getArray()
返回一个全局实例字段Object[] array
。
因此在此示例中:
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
如果此代码块周围没有锁,并且假设有两个线程试图添加一个元素,则在这种情况下,可能发生线程1和线程2都读取{的 same 值的情况。 {1}}并将新元素分配给相同的索引。
因此哪个线程在最后分配新值将覆盖另一个线程先前设置的值。
为进一步说明,假设线程1和线程2读取了相同的len
值,现在线程1继续从len
创建新数组并分配变量{{1 }}放在新数组的Arrays.copyOf(elements, len + 1)
位置。
在线程可以使用e
线程设置新数组之前,线程2同时使用相同的len
值继续此过程。尽管它将创建一个新的数组实例,但是设置新元素的索引将与线程一使用的setArray(newElements)
相同。
因此,当线程2使用len
将新数组设置为线程1之后的新值时,在len
th 索引处的较早数组值将被覆盖。通过线程2设置新元素。