java CopyOnWriteArrayList 和 synchronizedList 的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28979488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 14:29:04  来源:igfitidea点击:

Difference between CopyOnWriteArrayList and synchronizedList

javacollections

提问by Shashi Shankar

As per my understanding concurrent collection classes preferred over synchronized collections because the concurrent collection classes don't take a lock on the complete collection object. Instead they take locks on a small segment of the collection object.

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

But when I checked the addmethod of CopyOnWriteArrayList, we are acquiring a lock on complete collection object. Then how come CopyOnWriteArrayListis better than a list returned by Collections.synchronizedList? The only difference I see in the addmethod of CopyOnWriteArrayListis that we are creating copy of that array each time the addmethod is called.

但是当我检查 的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();
    }
}

采纳答案by Yellen

For write (add) operation, CopyOnWriteArrayList uses ReentrantLockand creates a backup copy of the data and the underlying volatile array reference is only updated via setArray(Any read operation on the list during before setArray will return the old data before add).Moreover, CopyOnWriteArrayList provides snapshot fail-safe iterator and doesn't throw ConcurrentModifficationException on write/ add.

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

But when I checked add method of CopyOnWriteArrayList.class, we are acquiring lock on complete collection object. Then how come CopyOnWriteArrayList is better than synchronizedList. The only difference I see in add method of CopyOnWriteArrayList is we are creating copy of that array each time add method get called.

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

  1. No, the lock is not on the entire Collection object. As stated above it is a ReentrantLockand it is different from the intrinsic object lock.
  2. The add method will always create a copy of the existing array and do the modification on the copy and then finally update the volatile reference of the array to point to this new array. And that's why we have the name "CopyOnWriteArrayList" - makes copy when you write into it.. This also avoids the ConcurrentModificationException
  1. 不,锁不在整个 Collection 对象上。如上所述,它是一个ReentrantLock,它不同于内部对象锁。
  2. add 方法将始终创建现有数组的副本并对副本进行修改,然后最终更新数组的易失性引用以指向这个新数组。这就是为什么我们有名称“CopyOnWriteArrayList” - 当你写入时进行复制..这也避免了 ConcurrentModificationException

回答by Stuart Marks

As per my understanding concurrent collection classes preferred over synchronized collection because concurrent collection classes don't take lock on complete collection object. Instead it takes lock on small segment of collection object.

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

This is true for some collections but not all. A map returned by Collections.synchronizedMaplocks the entire map around every operation, whereas ConcurrentHashMaplocks only one hash bucket for some operations, or it might use a non-blocking algorithm for others.

这适用于某些集合,但不是全部。由返回的Collections.synchronizedMap映射在每个操作周围锁定整个映射,而ConcurrentHashMap对于某些操作仅锁定一个哈希桶,或者它可能对其他操作使用非阻塞算法。

For other collections, the algorithms in use, and thus the tradeoffs, are different. This is particularly true of lists returned by Collections.synchronizedListcompared to CopyOnWriteArrayList. As you noted, both synchronizedListand CopyOnWriteArrayListtake a lock on the entire array during write operations. So why are the different?

对于其他集合,使用的算法以及权衡是不同的。对于由Collections.synchronizedList相比返回的列表尤其如此CopyOnWriteArrayList。正如你提到的,都synchronizedListCopyOnWriteArrayList走在写操作在整个阵列上的锁。那么为什么不同呢?

The difference emerges if you look at other operations, such as iterating over every element of the collection. The documentation for Collections.synchronizedListsays,

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

It is imperative that the user manually synchronize on the returned list when iterating over it:

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

    List list = Collections.synchronizedList(new ArrayList());
    ...
    synchronized (list) {
        Iterator i = list.iterator(); // Must be in synchronized block
        while (i.hasNext())
            foo(i.next());
    }

Failure to follow this advice may result in non-deterministic behavior.

不遵循此建议可能会导致不确定的行为。

In other words, iterating over a synchronizedListis notthread-safe unless you do locking manually. Note that when using this technique, all operations by other threads on this list, including iterations, gets, sets, adds, and removals, are blocked. Only one thread at a time can do anything with this collection.

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

By contrast, the doc for CopyOnWriteArrayListsays,

相比之下,文档 forCopyOnWriteArrayList说,

The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created.

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

Operations by other threads on this list can proceed concurrently, but the iteration isn't affected by changes made by any other threads. So, even though write operations lock the entire list, CopyOnWriteArrayListstill can provide higher throughput than an ordinary synchronizedList. (Provided that there is a high proportion of reads and traversals to writes.)

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

回答by Evgeniy Dorofeev

1) get and other read operation on CopyOnWriteArrayListare not synchronized.

1)get和其他read操作CopyOnWriteArrayList不同步。

2) CopyOnWriteArrayList's iterator never throws ConcurrentModificationExceptionwhile Collections.synchronizedList's iterator may throw it.

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