CopyOnWriteArrayList和synchronizedList之间的区别

时间:2015-03-11 05:50:10

标签: java collections

根据我的理解,并发集合类优先于同步集合,因为并发集合类不会锁定整个集合对象。相反,他们会对集合对象的一小部分进行锁定。

但是当我检查add的{​​{1}}方法时,我们正在获取完整集合对象的锁定。那么CopyOnWriteArrayList怎么比CopyOnWriteArrayList返回的列表更好?我在Collections.synchronizedList add方法中看到的唯一区别是,每次调用CopyOnWriteArrayList方法时,我们都会创建该数组的副本。

add

3 个答案:

答案 0 :(得分:10)

  

根据我的理解,并发集合类优先于同步集合,因为并发集合类不会锁定完整的集合对象。相反,它会锁定集合对象的一小部分。

对于某些收藏品而言并非如此。 Collections.synchronizedMap返回的映射将每个操作锁定整个映射,而ConcurrentHashMap仅锁定一个操作的一个散列桶,或者可能对其他操作使用非阻塞算法。

对于其他集合,使用的算法以及权衡因素是不同的。对于Collections.synchronizedListCopyOnWriteArrayList相比返回的列表尤其如此。如您所述,synchronizedListCopyOnWriteArrayList在写入操作期间锁定整个数组。那么为什么会有所不同?

如果您查看其他操作,例如迭代集合的每个元素,就会出现差异。 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 ConcurrentModificationExceptionCollections.synchronizedList的迭代器可能会抛出它。

答案 2 :(得分:7)

对于写入(添加)操作,CopyOnWriteArrayList使用ReentrantLock并创建数据的备份副本,并且仅通过setArray更新基础易失性数组引用(在setArray返回旧之前的列表上的任何读取操作添加之前的数据)。另外,CopyOnWriteArrayList提供了快照故障安全迭代器,并且在写/添加时不会抛出ConcurrentModifficationException。

  

但是当我检查了CopyOnWriteArrayList.class的add方法时,我们正在获取完整集合对象的锁定。那么CopyOnWriteArrayList如何比synchronizedList更好。我在CopyOnWriteArrayList的add方法中看到的唯一区别是我们每次调用add方法时都会创建该数组的副本。

  1. 不,锁定不在整个Collection对象上。如上所述,它是ReentrantLock,它与内在对象锁不同。
  2. add方法将始终创建现有数组的副本并对副本执行修改,然后最终更新数组的volatile引用以指向此新数组。这就是为什么我们有这个名字" CopyOnWriteArrayList" - 在写入时复制。这也避免了ConcurrentModificationException