我目前正在使用 ExecutorService 在 CopyOnWrite ArrayLists 中发送批量字符串以进行并行处理,其中处理这些列表的 Runnable 任务需要迭代列表并对每个字符串进行处理。
在遇到常规 ArrayList 的并发问题后,我尝试使用 CopyOnWriteArrayLists,因为它们是线程安全的,但我的结果现在不一致。也就是说,我在程序的每次运行中都会得到不同的结果,这表明在每个 Runnable 任务可以完全迭代它之前,数组列表的内容以某种方式发生了更改。
public static class BatchRunnable implements Runnable {
private CopyOnWriteArrayList<String> batch;
BatchRunnable(CopyOnWriteArrayList<String> batch){
this.batch = batch;
}
@Override
public void run(){
//iterate over batch and work with String elements
//make no modifications to batch
}
}
可运行任务不会对数组列表进行任何修改,它仅迭代列表并使用列表的 String 元素进行处理。
CopyOnWriteArrayList 唯一发生更改的地方是在每个新的 Runnable 任务实例化时。
当我传入单个字符串而不是批处理时,我得到了一致且正确的结果,但是当我开始在 String ArrayList 中使用批处理时,我得到了不一致的结果,这表明某些东西正在损害 CopyOnWriteArrayList 批处理的并发性,尽管它是据说是线程安全的。
感谢任何帮助,谢谢!
编辑:这是我的批处理正在构建的地方:
Runnable worker = null;
while((line = br.readLine()) != null) {
recordBatch.add(line);
if(recordBatch.size() == 100){
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch.clear();
}
}
executor.shutdown();
executor.awaitTermination(60,TimeUnit.SECONDS);
最佳答案
查看您的 while
循环:
while((line = br.readLine()) != null) {
recordBatch.add(line);
if(recordBatch.size() == 100){
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch.clear();
}
}
您正在将引用传递给所有 BatchRunnable
中的相同列表
。因此,一旦您在一个位置更改 list
,它就会反射(reflect)在所有引用中。因此,一旦您使用 recordBatch.clear()
清除列表,列表中的所有引用都是空的,即使是 BatchRunnable
中的引用也是如此。这就是为什么你得到不一致的结果。
您应该在 BatchRunnable
中传递 recordBatch
列表的副本
:
worker = new BatchRunnable(new ArrayList<String>(recordBatch));
关于java - CopyOnWriteArrayList迭代器与多线程不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17803515/