CopyOnWriteArrayList和synchronizedList之间的区别

Sha*_*kar 19 java collections

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

但是当我检查add方法时CopyOnWriteArrayList,我们正在获取完整集合对象的锁定.那么为什么CopyOnWriteArrayList比返回的列表更好Collections.synchronizedList?我在add方法中看到的唯一区别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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Stu*_*rks 12

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

对于某些集合而言并非如此.通过Collections.synchronizedMap在每个操作周围锁定整个映射而返回的映射,而ConcurrentHashMap对于某些操作仅锁定一个散列桶,或者对于其他操作可能使用非阻塞算法.

对于其他集合,使用的算法以及因此的权衡是不同的.对于通过Collections.synchronizedList比较返回的列表尤其如此CopyOnWriteArrayList.正如你提到的,都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());
    }
Run Code Online (Sandbox Code Playgroud)

不遵循此建议可能会导致非确定性行为.

换句话说,迭代一个synchronizedList不是线程安全的,除非你手动锁.请注意,使用此技术时,此列表中其他线程的所有操作(包括迭代,获取,设置,添加和删除)都将被阻止.一次只有一个线程可以对此集合执行任何操作.

相比之下,文件CopyOnWriteArrayList说,

"快照"样式迭代器方法在创建迭代器时使用对数组状态的引用.这个数组在迭代器的生命周期中永远不会改变,所以干扰是不可能的,并且保证迭代器不会抛出ConcurrentModificationException.自迭代器创建以来,迭代器不会反映列表的添加,删除或更改.

此列表上的其他线程的操作可以同时进行,但迭代不受任何其他线程所做更改的影响.因此,即使写入操作锁定整个列表,CopyOnWriteArrayList仍然可以提供比普通列表更高的吞吐量synchronizedList.(只要读取和遍历的比例很高.)

  • 很棒的答案 (2认同)

Evg*_*eev 7

1)get和其他读操作CopyOnWriteArrayList不同步.

2)CopyOnWriteArrayList迭代器永远不会,throws ConcurrentModificationExceptionCollections.synchronizedList迭代器可能会抛出它.


Yel*_*len 7

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

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

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

  • 另外,CopyOnWriteArrayList 不能用于使用 Iterator 修改列表,但 Collections.synchronizedList() 可以。 (2认同)