今天在学习CopyOnWriteArrayList原理的时候,发现一个问题。
CopyOnWriteArrayList更新操作是先复制原数组,然后在新数组上修改,之后再将原数组的引用指向新数组。
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(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);
}
return oldValue;
} finally {
lock.unlock();
}
}
在多线程条件下,分别是对原数组进行复制,即A线程复制原数组修改,然后更新;B线程复制原数组修改,然后更新。如果A线程修改的同时,B线程也进行操作。就会出现B线程修改的内容 覆盖了A线程的修改。
public void func() throws InterruptedException {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 3; i++) {
list.add(i);
}
Thread t1 = new Thread() {
@Override
public void run() {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.set(0, -1);
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.set(1, -1);
}
};
Thread t3 = new Thread() {
@Override
public void run() {
try {
sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.set(2, -1);
}
};
t1.start();
t2.start();
t3.start();
Thread.sleep(100);
System.out.println(list.toString());
}
运行3次,结果分别是
第一次:[0, -1, -1]
第二次:[0, -1, -1]
第三次:[-1, -1, -1]
按照我目前的理解,同一个代码,多次运行结果不一致,是线程不安全的。CopyOnWriteArrayList不能保证线程安全。
如果理解有误的地方,请大家提出讨论