ExecutorService和List <future <callable>&gt; with ConcurrentModificationException

时间:2016-03-03 20:15:31

标签: java concurrency

我有一个ExecutorService,它返回一个定义为List<Callable> callables=Collections.synchronizedList(new ArrayList<Callable>());

的List

当我第一次遇到问题时,我将它设为synchronizedList,因为我认为它是线程安全的,但问题仍然存在。

我遇到的问题是以下代码在Future<Object> next = i.next();下面抛出一个ConcurrentModificationException。值得注意的是,它在爆炸之前部分地通过了列表。

          ExecutorWorker snapshotExecutorWorker=new ExecutorWorker(this); 
      Iterator<Future<Object>> i= futures.iterator();
      while(i.hasNext()){
        Future<Object> next = i.next();//List<Callable>
        try {
            Object get = next.get();
            Class<? extends Object> aClass = get.getClass();
            System.out.println("Class= "+aClass);
         List<Callable> l=   (List)get;
         System.out.println("L.size= "+l.size());
       for(int a=0;a<l.size();a++){                 
        snapshotExecutorWorker.addTask(l.get(a));
       }

        } catch (InterruptedException ex) {
            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ExecutionException ex) {
            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
        }

      }

ExecutorWorker基本上是一个监视ExecutorCompletionService状态的SwingWorker。

public class ExecutorWorker extends SwingWorker<List<Future>, Object>   implements ExecutorInterface {

List<Future> results = new ArrayList();
ExecutorService executorService = Executors.newFixedThreadPool(100);
ExecutorCompletionService<Object> ecs = new ExecutorCompletionService<>(executorService);
List<Future<Object>> jobs = new ArrayList();
ProgressMonitor progressMonitor;
boolean isExecuting = true;
Monitor monitor;

public ExecutorWorker(Monitor f) {
    monitor = f;

}

public void addMonitor(Monitor f) {
    monitor = f;
}

/**
 *This method adds Callables to the Executor.
 * @param r
 * @return 
 */
@Override
public Future<?> addTask(Callable r) {
    Future futureValue = ecs.submit(r);
    monitor.addFuture(futureValue);
    System.out.println("Callable added in [ExecutorWorker]");
    jobs.add(futureValue);
    monitor.tasksAdded(true);
    return futureValue;
}

/**
 *This method returns a List containing the Future results. Use Future.get() to retrieve.
 * @return
 */
public List<Future> getResults() {
    return results;
}

@Override
protected void done() {

}

@Override
protected List<Future> doInBackground() throws ExecutionException, InterruptedException {
//        System.out.println("Executor: In Do In BackGround");
//        System.out.println("Jobs.size= " + jobs.size());

    for (int i = 0; i < this.jobs.size(); i++) {
        Future<Object> take = ecs.take();
        results.add(take);
        monitor.tasksCompleted(true);
        int v = (int) ((monitor.getCompletedTasks() /          this.monitor.getTotalTasks()) * 100);
        this.setProgress(v);
        String message = String.format("Processing " + (i + 1) + " of " +      jobs.size() + " Jobs. %d%%.\n", v);
        System.out.println("Message= " + message);
        progressMonitor.setNote(message);


    }

    return results;

}

public void setProgressMonitor(ProgressMonitor progressMonitor) {
    this.progressMonitor = progressMonitor;
}
}

2 个答案:

答案 0 :(得分:0)

Collections.synchronizedList()返回只包含同步方法的列表,它不会更新迭代器,因此仍然可以使用ConcurrentModificationException。

答案 1 :(得分:0)

如果在迭代时修改列表,迭代器将失效,并在下次使用时抛出ConcurrentModificationException(可能。请阅读有关“迭代器的故障快速属性”的详细信息)。

使它成为synchronizedList的所有操作都是将每个调用包装在同步块中的列表方法中。你不需要有多个线程来获得ConcurrentModificationException,你只需要做一些破坏迭代器的东西。

我相信你的具体例子,问题是addTask导致你正在迭代的列表被修改。

这个问题说明了Iterator :: remove()的使用,它允许在迭代它们时对某些类型的集合进行一些修改,尽管这与您的实际问题有些相似: Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop