首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CopyOnWriteArrayList与synchronizedList的区别

CopyOnWriteArrayList与synchronizedList的区别
EN

Stack Overflow用户
提问于 2015-03-11 13:50:10
回答 3查看 16.3K关注 0票数 26

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

但是当我检查CopyOnWriteArrayListadd方法时,我们正在获取一个完整集合对象的锁。那么为什么CopyOnWriteArrayListCollections.synchronizedList返回的列表更好呢?我在CopyOnWriteArrayListadd方法中看到的唯一区别是,每次调用add方法时,我们都会创建该数组的副本。

代码语言:javascript
复制
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();
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-03-11 15:11:40

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

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

  1. 否,锁不在整个集合对象上。如上所述,它是一个ReentrantLock,不同于固有的对象锁。
  2. add方法将始终创建现有数组的副本,并对副本进行修改,最后更新数组的易失性引用以指向这个新数组。这就是为什么我们有"CopyOnWriteArrayList“这个名字的原因--当你写的时候复制它..这也避免了ConcurrentModificationException
票数 14
EN

Stack Overflow用户

发布于 2015-03-12 06:26:23

根据我对

的理解,并发集合类比同步收集更受欢迎,因为并发集合类不会锁定整个集合对象。相反,它会锁定一小段集合对象。

这对于某些集合是正确的,但不是所有集合。Collections.synchronizedMap返回的映射锁定了每个操作周围的整个映射,而ConcurrentHashMap只为某些操作锁定了一个散列存储桶,或者它可能对其他操作使用非阻塞算法。

对于其他集合,使用的算法是不同的,因此权衡是不同的。与CopyOnWriteArrayList相比,Collections.synchronizedList返回的列表尤其如此。正如您所提到的,在写操作期间,synchronizedListCopyOnWriteArrayList都会锁定整个阵列。那么为什么会有所不同呢?

如果您查看其他操作,例如迭代集合的每个元素,就会发现差异。Collections.synchronizedList的文档说,

在迭代返回的列表时,用户必须手动同步该列表:

代码语言:javascript
复制
    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的医生说,

“快照”样式的迭代器方法使用对创建迭代器时数组状态的引用。此数组在迭代器的生命周期内不会更改,因此不可能发生干扰,并且迭代器保证不会抛出ConcurrentModificationException。自创建迭代器以来,迭代器将不会反映对列表的添加、删除或更改。

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

票数 26
EN

Stack Overflow用户

发布于 2015-03-11 13:57:02

1) CopyOnWriteArrayList上的get和其他读取操作不同步。

2) CopyOnWriteArrayList的迭代器从不throws ConcurrentModificationException,而Collections.synchronizedList的迭代器可能会抛出它。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28979488

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档