根据我的理解,并发集合类优先于同步集合,因为并发集合类不会锁定整个集合对象。相反,他们会对集合对象的一小部分进行锁定。
但是当我检查add
的{{1}}方法时,我们正在获取完整集合对象的锁定。那么CopyOnWriteArrayList
怎么比CopyOnWriteArrayList
返回的列表更好?我在Collections.synchronizedList
add
方法中看到的唯一区别是,每次调用CopyOnWriteArrayList
方法时,我们都会创建该数组的副本。
add
答案 0 :(得分:10)
根据我的理解,并发集合类优先于同步集合,因为并发集合类不会锁定完整的集合对象。相反,它会锁定集合对象的一小部分。
对于某些收藏品而言并非如此。 Collections.synchronizedMap
返回的映射将每个操作锁定整个映射,而ConcurrentHashMap
仅锁定一个操作的一个散列桶,或者可能对其他操作使用非阻塞算法。
对于其他集合,使用的算法以及权衡因素是不同的。对于Collections.synchronizedList
与CopyOnWriteArrayList
相比返回的列表尤其如此。如您所述,synchronizedList
和CopyOnWriteArrayList
在写入操作期间锁定整个数组。那么为什么会有所不同?
如果您查看其他操作,例如迭代集合的每个元素,就会出现差异。 Collections.synchronizedList
的文档说明了
当迭代时,用户必须手动同步返回的列表:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
不遵循此建议可能会导致非确定性行为。
换句话说,迭代synchronizedList
不线程安全,除非您手动锁定。请注意,使用此技术时,此列表中其他线程的所有操作(包括迭代,获取,设置,添加和删除)都将被阻止。一次只有一个线程可以对此集合执行任何操作。
相比之下,CopyOnWriteArrayList
的文档说明了
“snapshot”样式迭代器方法在创建迭代器时使用对数组状态的引用。这个数组在迭代器的生命周期中永远不会改变,所以干扰是不可能的,并且保证迭代器不会抛出
ConcurrentModificationException
。自迭代器创建以来,迭代器不会反映列表的添加,删除或更改。
此列表中其他线程的操作可以同时进行,但迭代不受任何其他线程所做更改的影响。因此,即使写操作锁定整个列表,CopyOnWriteArrayList
仍然可以提供比普通synchronizedList
更高的吞吐量。 (只要读取和遍历的比例很高。)
答案 1 :(得分:7)
1)get {和CopyOnWriteArrayList
上的其他读操作不同步。
2)CopyOnWriteArrayList
' s迭代器永远不会throws ConcurrentModificationException
而Collections.synchronizedList
的迭代器可能会抛出它。
答案 2 :(得分:7)
对于写入(添加)操作,CopyOnWriteArrayList使用ReentrantLock并创建数据的备份副本,并且仅通过setArray更新基础易失性数组引用(在setArray返回旧之前的列表上的任何读取操作添加之前的数据)。另外,CopyOnWriteArrayList提供了快照故障安全迭代器,并且在写/添加时不会抛出ConcurrentModifficationException。
但是当我检查了CopyOnWriteArrayList.class的add方法时,我们正在获取完整集合对象的锁定。那么CopyOnWriteArrayList如何比synchronizedList更好。我在CopyOnWriteArrayList的add方法中看到的唯一区别是我们每次调用add方法时都会创建该数组的副本。