CopyOnWriteArrayList如何可以线程安全?

我已经看了一下CopyOnWriteArrayList OpenJDK源代码 ,看起来所有的写操作都被相同的锁保护,读操作根本不受保护。 据我所知,在JMM下,所有对variables(读取和写入)的访问都应该受到锁的保护,否则可能会发生重新sorting效应。

例如, set(int, E)方法包含这些行(在locking下):

 /* 1 */ int len = elements.length; /* 2 */ Object[] newElements = Arrays.copyOf(elements, len); /* 3 */ newElements[index] = element; /* 4 */ setArray(newElements); 

另一方面get(int)方法只return get(getArray(), index);

在我对JMM的理解中,这意味着如果语句1-4像1-2(新)-4-2(copyOf)-3一样被重新sorting, get可能会观察到数组处于不一致状态。

我是否不正确地理解JMM,或者有什么解释说明为什么CopyOnWriteArrayList是线程安全的?

如果你看看底层的数组引用,你会看到它被标记为volatile 。 当发生写操作时(例如在上面的提取中),这个volatile引用只会在最后的语句中通过setArray更新。 直到这一点,任何读取操作都将从数组的旧副本中返回元素。

重要的一点是数组更新是一个primefaces操作 ,因此读取将始终看到数组处于一致状态。

仅对写操作取出锁的优点是提高了读操作的吞吐量:这是因为CopyOnWriteArrayList写操作可能会非常慢,因为它们涉及复制整个列表。

获取数组引用是一个primefaces操作。 因此,读者将会看到旧的数组或新的数组 – 无论哪种状态都是一致的。 ( set(int,E)在设置引用之前计算新的数组内容,因此在进行分配时数组是一致的。)

数组引用本身被标记为volatile以便读者不需要使用锁来查看对引用数组的更改。 (编辑:此外, volatile保证分配不重新sorting,这将导致分配时,arrays可能处于不一致的状态。

写locking是为了防止并发修改,这可能导致arrays持有不一致的数据或更改丢失。