我如何创建一个并发的列表实例,在那里我可以通过索引访问元素?JDK是否有我可以使用的类或工厂方法?


当前回答

免责声明:这个答案发表于2011年,在JDK 5之前,也在更高级和最优的并发api之前。因此,虽然下面的方法可以工作,但它不是最好的选择。


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

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

其他回答

大多数情况下,如果您需要并发列表,则它位于模型对象内部(因为您不应该使用抽象数据类型(如列表)来表示应用程序模型图中的节点),或者它是特定服务的一部分,您可以自己同步访问。

class MyClass {
  List<MyType> myConcurrentList = new ArrayList<>();
  void myMethod() {
    synchronzied(myConcurrentList) {
      doSomethingWithList;
    }
  }
}

通常这就足够让你继续下去了。如果你需要迭代,迭代列表的一个副本,而不是列表本身,并且只同步你复制列表的部分,而不是在你迭代它的时候。

另外,当并发处理一个列表时,通常要做的事情不仅仅是添加、删除或复制,这意味着该操作变得足够有意义,以保证它自己的方法,并且该列表成为一个特殊类的成员,仅表示这个具有线程安全行为的特定列表。

即使我同意需要一个并发列表实现和Vector / collections . synchronizelist (list)不做的技巧,你肯定需要像compareAndAdd或compareAndRemove或get(…, ifAbsentDo),即使您有ConcurrentList实现,开发人员在使用并发列表(和映射)时也经常会因为没有考虑真正的事务而引入错误。

在这些场景中,事务对于与并发ADT(抽象数据类型)交互的预期目的来说太小,总是导致我将列表隐藏在一个特殊的类中,并在方法级别上使用synchronized同步访问这个类对象方法。这是确保交易正确的唯一方法。

我已经看到了太多的错误,不能用其他方法来做——至少如果代码很重要,处理一些像金钱或安全或保证一些服务质量措施(例如至少发送一次且只发送一次消息)。

因为获取位置和从给定位置获取元素的行为自然需要一些锁定(您不能让列表在这两个操作之间发生结构变化)。

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

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

ConcurrentLinkedQueue

如果您不关心是否具有基于索引的访问,而只想要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
}

你有这些选择:

Collections.synchronizedList(): you can wrap any List implementation (ArrayList, LinkedList or a 3rd-party list). Access to every method (reading and writing) will be protected using synchronized. When using iterator() or enhanced for loop, you must manually synchronize the whole iteration. While iterating, other threads are fully blocked even from reading. You can also synchronize separately for each hasNext and next calls, but then ConcurrentModificationException is possible. CopyOnWriteArrayList: it's expensive to modify, but wait-free to read. Iterators never throw ConcurrentModificationException, they return a snapshot of the list at the moment of iterator creation even if the list is modified by another thread while iterating. Useful for infrequently updated lists. Bulk operations like addAll are preferred for updates - the internal array is copied less many times. Vector: very much like synchronizedList(new ArrayList<>()), but iteration is synchronized too. However, iterators can throw ConcurrentModificationException if the vector is modified by another thread while iterating.

其他选项:

Queue or Deque might be an alternative if you only add/remove at the ends of the list or iterate it. Queue allows only adding at one end and removing from the other end, Deque allows adding and removing on both ends. There's no access by index. There are multiple implementations with better concurrency properties than any List can provide. Look at "All Known Implementing Classes" in the Queue javadoc, those implementations that are in the java.util.concurrent package are concurrent. You can also have a look at JCTools, it contains faster queue implementations specialized for single consumer or single producer. Collections.unmodifiableList(): wait-free, thread-safe, but non-modifiable List.of & List.copyOf: Another non-modifiable list in Java 9 and later.

免责声明:这个答案发表于2011年,在JDK 5之前,也在更高级和最优的并发api之前。因此,虽然下面的方法可以工作,但它不是最好的选择。


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

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