Java的JDK中是否有并发列表?


277

如何创建并发的List实例,可以在其中按索引访问元素?JDK是否可以使用任何类或工厂方法?


28
为什么不建设性?.Net中找不到几个建议的CopyOnWriteArrayList。您可以说两个问题都相互关联,但是并不能解决这个问题!!!
2011年

1
我不知道为什么贾罗德·罗伯森(Jarrod Roberson)会认为,采纳斯蒂芬(Stephan)所做的详细编辑并将其还原为措辞不佳的原始问题是一个好主意。贾罗德的答案仍然是一个完全可以接受的答案。实际上,CopyOnWriteArrayList是JDK中唯一实现List的并发类。困惑...
马特·帕塞尔

10
因为已接受的答案是针对原始问题的,而斯蒂芬则提出了一个与源代码完全无关的问题,而原始代码并未在任何地方完全改变该问题,从而产生了更多的答案,暗示了除List原始问题之外的其他内容说是被认为是故意破坏的要求。主持人已锁定该问题,因为有人抱怨答案未回答该问题的恶意版本。

1
// locked/ closed先前的评论

8
没有理由要解决这个问题。它询问有关JDK中的类的信息,这与搜索库完全不同。它是Java的基础。
maaartinus

Answers:



166

如果您不关心具有基于索引的访问权限,而只想要List的保留插入顺序的特性,则可以考虑使用java.util.concurrent.ConcurrentLinkedQueue。由于它实现了Iterable,因此在添加完所有项目之后,您可以使用增强的for语法遍历内容:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
我认为简化的for语句(:)称为foreach:docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka 2014年

2
@ AlikElzin-kilaka你是对的。我认为这个名字一直困扰着我,因为实际的语法不包含“每个”一词,但是我将更新答案以使用正式名称。:)
Matt Passell 2014年

3
@ AlikElzin-kilaka Nitpicking,但是根据JLS版本8,它被称为“增强声明”。在Java教程中也是如此
罗兰

1
@罗兰绝对不挑剔。Java中的“ for each”和“ enhanced for”之间(现在)有所区别。
hfontanez

2
@罗兰间接。我相信他们将“每个循环”重命名为“增强”,以消除Stream.forEach和现在称为“增强的”之间的混淆。
hfontanez

128

如果您需要的只是简单的调用同步,则可以很好地使用Collections.synchronizedList(List)

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
的结果synchronizedList是“同步的”,而不是“并发的”。许多基本操作(基于索引)本身不是原子的,这是一个基本问题,需要成为更大的互斥结构的一部分。

6
IMO,asing a Vector比更加简单Collections.synchronizedList(new ArrayList<Object>())
Stephan

8
syncedList的结果是“ synchronized”,而不是“ concurrent”。
Kanagavelu Sugumar 2014年

46

因为获取位置并从给定位置获取元素的动作自然需要一些锁定(您不能使列表在这两个操作之间具有结构上的变化)。

并发收集的基本思想是,每个操作本身都是原子的,可以在没有显式锁定/同步的情况下完成。

因此,在预期并发访问的情况下,将元素n从给定的位置List作为原子操作放置就没有太大意义。


6
约阿希姆,我想你真是头疼。以只读列表作为并发列表为例。从列表中将元素放在位置N不仅有意义,而且是问题的概括。因此,不可变列表(小写字母L)将是一个很好的例子,但它不是列表(大写字母L)。CopyOnWriteArrayList是并发的,但是很多人不喜欢这种性能。沿着绳索(绳索)的解决方案可能是一个不错的选择。
johnstosh

1
很好。但是OP将要使用的List可能有非常特定的用法。例如,它可以在并发环境中填充,然后“锁定”(无论它是什么意思),然后由索引安全地访问。因此,在填充此类List的第一阶段,仍将需要线程安全的实现。不幸的是,OP并未具体说明如何使用他要查找的列表。
igor.zh'18

13

您有以下选择:

  • Collections.synchronizedList():您可以包装任何List实现(ArrayListLinkedList或第三方列表)。使用可以保护对每种方法(读写)的访问synchronized。使用iterator()或增强for循环时,必须手动进行同步;在迭代时,其他线程甚至被完全阻止读取。您也可以分别为每个呼叫hasNext和每个next呼叫进行同步,但是可以同步ConcurrentModificationException

  • CopyOnWriteArrayList:修改成本很高,但无需等待阅读。迭代器从不抛出ConcurrentModificationException,他们在创建迭代器时返回列表的快照,即使列表在迭代时被另一个线程修改了。对于不经常更新的列表很有用。像批量操作之类addAll的操作对于更新是首选的-内部数组被复制的次数较少。

  • Vector:非常像synchronizedList,但是迭代也是同步的。但是,ConcurrentModificationException如果向量在迭代时被另一个线程修改,则迭代器会抛出。

其他选项:

  • Collections.unmodifiableList():无锁,线程安全,但不可修改
  • Queue或者Deque如果您仅在列表的末尾添加/删除并迭代列表,则可以选择替代方法。没有按索引的访问,也没有在任意位置进行添加/删除的操作。他们有多个并发实现,它们具有更好的性能和更好的并发访问,但这超出了此问题的范围。您还可以查看JCTools,它们包含更多针对单个使用者或单个生产者的性能更高的队列实现。

4

在此处输入图片说明

CopyOnWriteArrayList是ArrayList的线程安全变体,其中所有可变操作(添加,设置等)都通过对基础数组进行全新复制来实现。

CopyOnWriteArrayList是同步List实现List接口及其java.util.concurrent包的一部分及其线程安全集合的并发替代方法。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList是故障安全的,并且在迭代期间使用ArrayList的单独副本修改基础CopyOnWriteArrayList时,不会引发ConcurrentModificationException。

通常,这太昂贵了,因为复制数组涉及每个更新操作,因此会创建克隆副本。仅在频繁读取操作时,CopyOnWriteArrayList是最佳选择。

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.