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持有不一致的数据或更改丢失。