java CopyOnWriteArrayList 如何是线程安全的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2950871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can CopyOnWriteArrayList be thread-safe?
提问by Fixpoint
I've taken a look into OpenJDK source codeof CopyOnWriteArrayListand it seems that all write operations are protected by the same lock and read operations are not protected at all. As I understand, under JMM all accesses to a variable (both read and write) should be protected by lock or reordering effects may occur.
我查看了OpenJDK 的源代码,CopyOnWriteArrayList似乎所有写操作都受同一个锁保护,而读操作根本不受保护。据我了解,在 JMM 下,对变量的所有访问(读取和写入)都应受锁定保护,否则可能会发生重新排序效果。
For example, set(int, E)method contains these lines (under lock):
例如,set(int, E)方法包含这些行(锁定):
/* 1 */ int len = elements.length;
/* 2 */ Object[] newElements = Arrays.copyOf(elements, len);
/* 3 */ newElements[index] = element;
/* 4 */ setArray(newElements);
The get(int)method, on the other hand, only does return get(getArray(), index);.
get(int)另一方面,该方法仅执行return get(getArray(), index);.
In my understanding of JMM, this means that getmay observe the array in an inconsistent state if statements 1-4 are reordered like 1-2(new)-4-2(copyOf)-3.
在我对 JMM 的理解中,这意味着get如果语句 1-4 像 1-2(new)-4-2(copyOf)-3 那样重新排序,则可能会观察到处于不一致状态的数组。
Do I understand JMM incorrectly or is there any other explanations on why CopyOnWriteArrayListis thread-safe?
我是否错误地理解了 JMM 或者是否有其他关于CopyOnWriteArrayList线程安全的解释?
回答by Adamski
If you look at the underlying array reference you'll see it's marked as volatile. When a write operation occurs (such as in the above extract) this volatilereference is only updated in the final statement via setArray. Up until this point any read operations will return elements from the old copyof the array.
如果您查看底层数组引用,您会看到它被标记为volatile. 当发生写操作时(例如在上面的摘录中),此volatile引用仅在最终语句中通过setArray. 直到此时,任何读取操作都将从数组的旧副本中返回元素。
The important point is that the array update is an atomic operationand hence reads will always see the array in a consistent state.
重要的一点是数组更新是一个原子操作,因此读取将始终看到数组处于一致状态。
The advantage of only taking out a lock for write operations is improved throughput for reads: This is because write operations for a CopyOnWriteArrayListcan potentially be very slow as they involve copying the entire list.
只为写操作取出锁的优点是提高了读取的吞吐量:这是因为 a 的写操作CopyOnWriteArrayList可能会非常慢,因为它们涉及复制整个列表。
回答by mdma
Getting the array reference is an atomic operation. So, readers will either see the old array or the new array - either way the state is consistent. (set(int,E)computes the new array contents before setting the reference, so the array is consistent when the asignment is made.)
获取数组引用是一个原子操作。因此,读者要么看到旧数组,要么看到新数组——无论哪种方式,状态都是一致的。(set(int,E)在设置引用之前计算新的数组内容,因此在进行赋值时数组是一致的。)
The array reference itself is marked as volatileso that readers do not need to use a lock to see changes to the referenced array. (EDIT: Also, volatileguarantees that the assignment is not re-ordered, which would lead to the assignment being done when the array is possibly in an inconsistent state.)
数组引用本身被标记为volatile这样读者不需要使用锁来查看对引用数组的更改。(编辑:另外,volatile保证分配不会重新排序,这将导致在数组可能处于不一致状态时完成分配。)
The write lock is required to prevent concurrent modification, which may result the array holding inconsistent data or changes being lost.
需要写锁来防止并发修改,这可能导致数组持有不一致的数据或更改丢失。
回答by nagendra547
So according to Java 1.8, following are the declarations of arrayand lockin CopyOnWriteArrayList.
因此,根据对Java 1.8,以下是声明数组,并锁定在的CopyOnWriteArrayList。
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
Following is definition of addmethod of CopyOnWriteArrayList
下面是CopyOnWriteArrayList的add方法的定义
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();
}
}
As @Adamski has already mentioned arrayis volatile and only updated via the setArray method . After that, if all the read only calls are made, and so they would be getting the updated value and hence array is always consistent here.
正如@Adamski 已经提到的,数组是可变的,只能通过 setArray 方法更新。之后,如果进行了所有只读调用,那么它们将获得更新的值,因此数组在此处始终保持一致。

