从Java中的Set中并发迭代和删除

时间:2012-07-23 08:50:09

标签: java multithreading

我有一组预先填充的字符串。我想迭代项目,并在迭代时,我需要“做工作”,这也可能从集合中删除项目。我想为每个项目的“do work”生成一个新线程。请注意,在“工作”期间,只有部分项目会从集合中删除。

现在我有以下问题,

我可以通过简单地使用Collections.synchronizedSet(new HashSet())来实现这一点; ?我猜这会抛出ConcurrentModificationException,因为我在迭代时从列表中删除项目。如何在没有一致性问题的情况下有效地实现上述行为?

谢谢!

4 个答案:

答案 0 :(得分:4)

我会使用ExecutorService

ExecutorService es = Executors.newFixedThreadPool(n);
List<Future<String>> toRemove = new ARraysList<>();
for(String s: set)
   toRemove.add(es.submit(new Task(s)));
for(Future<String> future : toRemove()) {
   String s = future.get();
   if (s != null)
       set.remove(s);
}

这避免了需要以多线程方式访问集合。

答案 1 :(得分:1)

使用主生产者线程,该线程将从集合中删除元素,并将它们提供给消费者线程。消费者线程无需“亲自”删除项目。

答案 2 :(得分:1)

是的,SynchronisedSet仍然会抛出ConcurrentModificationExceptions。

试试这个:

Set s = Collections.newSetFromMap(new ConcurrentHashMap())

当多个线程正在访问和修改它时,ConcurrentHashMap不应抛出ConcurrentModificationException。

答案 3 :(得分:0)

该方法取决于集合中数据与成功完成操作之间的关系。

从Set中删除与任务执行结果无关

如果您不关心线程执行的实际结果,您可以在调度任务时查看集合并删除每个项目(您已经有一些示例)

仅在任务执行成功完成时从“设置”中删除

如果从集合中删除应该是执行成功的事务,您可以使用Future来收集有关任务执行成功的信息。这样,只有成功执行的项目才会从原始集合中删除。无需同时访问Set结构,因为您可以使用FutureExecutorService将执行与检查分开。例如:

// This task will execute the job and, 
// if successful, return the string used as context
class Task implements Callable<String> {
    final String target;
    Task(String s) {
        this.target = s; 
    }
    @Override
    public String call() throws Exception {
        // do your stuff
        // throw an exception if failed 
        return target;
    }
}

这就是它的用法:

ExecutorService executor;

Set<Callable<String>> myTasks = new HashSet<Callable<String>>();
for(String s: set) {
   myTasks.add(new Task(s));
}
List<Future<String>> results = executor.invoqueAll(myTasks);
for (Future<String> result:results) {
    try {
        set.remove(result.get());
    } catch (ExecutionException ee) {
        // the task failed during execution - handle as required
    } catch (CancellationException ce) {
        // the task was cancelled - handle as required
    }
}