在多线程程序中使用迭代器时获取并发修改异常

时间:2017-03-16 21:21:04

标签: java multithreading iterator threadpool java.util.concurrent

我的代码是:

class Processor implements Runnable {

    private int id;
    private Integer interaction;
    private Set<Integer> subset;
    private volatile static AtomicBoolean notRemoved = new AtomicBoolean(true);

    public Object<E> dcp;
    public Iterator<Integer> iterator;



    public Processor(int id, Integer interaction, Set<Integer> subset, Object<E> dcp, Iterator<Integer> iterator) {
        this.id = id;
        this.interaction = interaction;
        this.subset= subset;
        this.dcp = dcp;
        this.iterator = iterator;
    }

    public void run() {
        while (Processor.notRemoved.get()){
            System.out.println("Starting: " + this.subset);
            if (this.dcp.PA.contains(this.interaction)){
                this.subset.add(this.interaction);
                this.dcp.increaseScore(this.subset);
                if (!this.subset.contains(this.interaction) && Processor.notRemoved.get()){
                    Processor.notRemoved.set(false);
                    iterator.remove();
                }
            }

            System.out.println("Completed: " + this.id);
        }
    }
}


public class ConcurrentApp {

    public void multiThreadProgram (Object<E> dcp, int threads) {

        ExecutorService executor = Executors.newFixedThreadPool(threads);

        int i =1;
        while ((dcp.PA.size() > i) && (i <= dcp.R)){
            for (Iterator<Integer> iterator = dcp.PA.iterator(); iterator.hasNext();){
                Integer interaction = iterator.next();
                ArrayList<Integer> removed = new ArrayList<Integer>(dcp.PA);
                removed.remove(interaction);
                ArrayList<Set<Integer>> subsets = dcp.getSubsets(removed, i);
                for (int j = 0; j< subsets.size(); j++){
                    try {
                        executor.submit(new Processor(j, interaction, subsets.get(j), dcp, iterator));
                    } catch (RejectedExecutionException e){
                        System.out.println("Task was rejected");
                    }   
                }
            }
            System.out.println("All tasks completed");
            i++;
        }
        executor.shutdown();
        System.out.println("All tasks submitted");
        try {
            executor.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我在java.util.ArrayList的java.util.ArrayList $ Itr.checkForComodification(未知来源)获取 java.util.ConcurrentModificationException $ Itr.next(未知来源)......

但是,我认为通过使用迭代器,我可以避免在同时修改我的集合时迭代我的集合的问题。我在我的程序的非多线程实现中没有这个问题,所以我假设这与多个线程与iterator做某事有关。有没有人有任何想法?

2 个答案:

答案 0 :(得分:2)

无论您是否使用迭代器,Java的ArrayList都不是线程安全的。

来自Javadoc

  

如果多个线程同时访问ArrayList实例,并且在   至少有一个线程在结构上修改列表,它必须是   外部同步。

...

  

此类的迭代器和listIterator返回的迭代器   方法是快速失败的:如果列表在结构上被修改了   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己删除或添加方法,迭代器将抛出一个   ConcurrentModificationException的

看看这个question,讨论线程安全的替代方案。基本上,您需要使用锁(例如synchronized块)或合适的线程安全列表。

答案 1 :(得分:1)

  

我认为通过使用迭代器,我可以避免在同时修改我的集合时迭代我的集合的问题。

关于ListIterator<T>的情况属实,而不仅仅是Iterator<T> s。为了避免删除ConcurrentModificationException,必须遵循以下几点:

  • ListIterator<T>必须支持remove() - 此操作是可选的
  • 您的代码必须通过调用list iterator&#39; s remove() 来删除该项目 - 直接从列表中删除不起作用
  • 通过其他迭代器必须没有并发删除 - ListIterator<T>负责通过同一个迭代器对象进行删除;当你的代码从不同的线程中同时删除项目时,它们没有帮助。