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

时间:2012-07-19 03:36:41

标签: java multithreading linked-list concurrentmodification

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

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

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

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

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

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

  

有没有办法阻止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
    }
}

答案 2 :(得分:0)

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

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

你能分成N个名单吗?

答案 3 :(得分:0)

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

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

A - > B - > C - > d

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

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