如何创建并发的List实例,可以在其中按索引访问元素?JDK是否可以使用任何类或工厂方法?
List
原始问题之外的其他内容说是被认为是故意破坏的要求。主持人已锁定该问题,因为有人抱怨答案未回答该问题的恶意版本。
如何创建并发的List实例,可以在其中按索引访问元素?JDK是否可以使用任何类或工厂方法?
List
原始问题之外的其他内容说是被认为是故意破坏的要求。主持人已锁定该问题,因为有人抱怨答案未回答该问题的恶意版本。
Answers:
java.util.concurrent中有一个并发列表实现。特别是CopyOnWriteArrayList。
如果您不关心具有基于索引的访问权限,而只想要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
}
:
)称为foreach:docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
如果您需要的只是简单的调用同步,则可以很好地使用Collections.synchronizedList(List):
List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
Vector
比更加简单Collections.synchronizedList(new ArrayList<Object>())
。
因为获取位置并从给定位置获取元素的动作自然需要一些锁定(您不能使列表在这两个操作之间具有结构上的变化)。
并发收集的基本思想是,每个操作本身都是原子的,可以在没有显式锁定/同步的情况下完成。
因此,在预期并发访问的情况下,将元素n
从给定的位置List
作为原子操作放置就没有太大意义。
您有以下选择:
Collections.synchronizedList()
:您可以包装任何List
实现(ArrayList
,LinkedList
或第三方列表)。使用可以保护对每种方法(读写)的访问synchronized
。使用iterator()
或增强for循环时,必须手动进行同步;在迭代时,其他线程甚至被完全阻止读取。您也可以分别为每个呼叫hasNext
和每个next
呼叫进行同步,但是可以同步ConcurrentModificationException
。
CopyOnWriteArrayList
:修改成本很高,但无需等待阅读。迭代器从不抛出ConcurrentModificationException
,他们在创建迭代器时返回列表的快照,即使列表在迭代时被另一个线程修改了。对于不经常更新的列表很有用。像批量操作之类addAll
的操作对于更新是首选的-内部数组被复制的次数较少。
Vector
:非常像synchronizedList
,但是迭代也是同步的。但是,ConcurrentModificationException
如果向量在迭代时被另一个线程修改,则迭代器会抛出。
其他选项:
Collections.unmodifiableList()
:无锁,线程安全,但不可修改Queue
或者Deque
如果您仅在列表的末尾添加/删除并迭代列表,则可以选择替代方法。没有按索引的访问,也没有在任意位置进行添加/删除的操作。他们有多个并发实现,它们具有更好的性能和更好的并发访问,但这超出了此问题的范围。您还可以查看JCTools,它们包含更多针对单个使用者或单个生产者的性能更高的队列实现。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();
}
}