繁体   English   中英

LinkedList迭代器抛出并发修改异常

[英]LinkedList Iterator throwing Concurrent Modification Exception

有没有办法阻止ListIterator抛出ConcurrentModificationException? 这就是我想要做的:

  • 使用具有要经常执行的特定方法的一堆对象创建LinkedList。
  • 有一定数量的线程(比如N),所有这些线程都负责执行LinkedList中对象的所述方法。 例如,如果列表中有k个对象,则线程n将执行列表中第n个对象的方法,然后转到第n + N个对象,然后转到第n + 2N个等,直到它循环回到开头。

这里的问题在于检索这些对象。 我显然会使用ListIterator来完成这项工作。 但是,由于将根据文档抛出的ConcurrentModificationException,我预测这不会走得太远。 我希望列表可以修改,并且迭代器不关心。 实际上,预计这些对象将创建并销毁列表中的其他对象。 我想过几个解决方法:

  • 创建并销毁新迭代器以检索给定索引处的对象。 然而,这是O(n),是不合需要的。
  • 改为使用ArrayedList; 然而,这也是不可取的,因为删除是O(n)并且列表中有时需要扩展(并且可能会收缩?)。
  • 编写我自己的LinkedList类。 不想。

因此,我的问题。 有没有办法阻止ListIterator抛出ConcurrentModificationException?

你似乎关心性能。 您是否真的测量过使用O(n)vs O(1)算法的性能? 根据您正在进行的操作以及执行操作的频率,只需使用线程安全的CopyOnWriteArrayList即可。 它的迭代器也是线程安全的。

主要的性能拖累是变异操作(设置,添加,删除......):每次重新创建一个新列表。

但是,对于大多数应用程序而言,性能足够好。 我个人会尝试使用它,配置我的应用程序来检查性能是否足够好,如果是的话继续前进。 如果不是,您将需要找到其他方法。

有没有办法阻止ListIterator抛出ConcurrentModificationException?

您以这种方式询问此问题表明缺乏对如何正确使用线程来提高应用程序性能的理解。

使用线程的整个目的是将处理和IO划分为可以并行执行的独立可运行实体 - 彼此独立 如果您要将线程分配到同一个LinkedList上的所有工作,那么您很可能会有性能损失或最小增益,因为保持LinkedList每个线程“视图”同步所需的同步开销将抵消任何增益由于并行执行。

这个问题应该是“怎么我停止ConcurrentModificationException ”,应该是“我如何使用线程来提高对象的列表的处理”。 这是正确的问题。

要与多个线程并行处理对象集合,您应该使用ExecutorService线程池。 您可以使用以下代码创建池。 然后, LinkedList每个条目(在此示例中为Job )将由池中的线程并行处理。

// create a thread pool with 10 workers
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// submit each of the objects in the list to the pool
for (Job job : jobLinkedList) {
    threadPool.submit(new MyJobProcessor(job));
}
// once we have submitted all jobs to the thread pool, it should be shutdown
threadPool.shutdown();
// wait for the thread-pool jobs to finish
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
synchronized (jobLinkedList) {
    // not sure this is necessary but we need to a memory barrier somewhere
}
...
// you wouldn't need this if Job implemented Runnable
public class MyJobProcessor implements Runnable {
    private Job job;
    public MyJobProcessor(Job job) {
        this.job = job;
    }
    public void run() {
        // process the job
    }
}

您可以使用一个Iterator来扫描列表,并使用Executor通过传递给一个线程池来对每个对象执行工作。 这很容易。 以这种方式打包工作单元会产生开销。 您仍然必须小心使用Iterator方法来修改列表,但这可能会简化问题。

或者你可以一次完成你的工作,然后在下一个列表修改?

你能分成N个名单吗?

请参阅@assylias的答案 - 他的建议很好。 我想补充一点,如果您决定编写自己的链表类,则需要仔细考虑如何使其成为线程安全的。

如果多个线程试图同时修改它,请考虑列表可能被破坏的所有方式。 仅锁定1或2个节点是不够的 - 例如,请采用以下列表:

A - > B - > C - > D.

想象一下,一个线程试图删除B,就像另一个线程正在删除C.要删除B,来自A的链接需要“跳”到B到C.但是如果C在那个时候不再是列表的一部分呢? 同样,要删除C,需要将B中的链接更改为跳转到D,但是如果B已经从列表中删除了那么该怎么办? 当节点同时添加到列表的附近部分时,会出现类似的问题。

如果每个节点有1个锁,并且在执行“删除”操作(要删除的节点以及它之前和之后的节点)时锁定3个节点,我认为它将是线程安全的。 您还需要仔细考虑添加节点时以及遍历列表时必须锁定哪些节点。 为了避免死锁,您需要确保始终以常量顺序获取锁定,并且在遍历列表时,您需要使用“手动”锁定(这会阻止使用普通的Java监视器 - 您需要明确锁定对象)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM