我正在使用ExecutorService在CopyOnWrite ArrayLists中发送批量的字符串并行处理,其中处理这些列表的Runnable任务需要遍历列表并对每个字符串进行处理。
在使用常规ArrayLists遇到并发问题后,我尝试使用CopyOnWriteArrayLists,因为它们是线程安全的,但是我的结果现在不一致了。也就是说,我在程序的每次运行中得到不同的结果,这表明在每个Runnable taks可以完全迭代之前,arraylist的内容会以某种方式改变。
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
}
}
runnable任务对arraylist进行NO修改,它只遍历列表并使用列表中的String元素进行处理。
更改CopyOnWriteArrayList的唯一位置是使用每个新的Runnable任务进行实例化。
当我传入单个字符串而不是批处理时,我得到了一致且正确的结果,但是当我开始在String ArrayLists中使用批处理时,我得到了不一致的结果,表明有些东西会影响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);
答案 0 :(得分:2)
查看您的while
循环:
while((line = br.readLine()) != null) {
recordBatch.add(line);
if(recordBatch.size() == 100){
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch.clear();
}
}
您将所有list
中的引用传递给同一BatchRunnable
。因此,只要您在一个地方更改list
,它就会反映在所有参考中。因此,一旦您使用recordBatch.clear()
清除列表,列表对于所有引用都是空的,甚至是您在BatchRunnable
中的列表。这就是你得到不一致结果的原因。
您应该在copy
中传递recordBatch
个BatchRunnable
列表:
worker = new BatchRunnable(new ArrayList<String>(recordBatch));
答案 1 :(得分:1)
您将批次传递给BatchRunnable
后清除。
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch.clear(); // You clear all the list
因此执行程序将处理列表中的任何内容但是如果到达clear()行(并且因为执行程序在不同的线程上运行,这可能发生在BatchRunnable
完成之前),那么列表将为空(或包含下一批!)并且工作批将具有不一致的列表。
当您将列表传递给工作人员时,您传递的是引用而不是副本!因此,要么为每个批次复制批次或创建一个新批次:
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch = new CopyOnWriteArrayList<String>();
答案 2 :(得分:0)
我猜你使用迭代器来浏览元素。迭代器提供构造迭代器时列表状态的快照。遍历迭代器时不需要同步。
因此,在您的情况下,您应该在构造函数中获取迭代器,或者复制CopyOnWriteArrayList
。
答案 3 :(得分:0)
如果您遇到简单问题ArrayList
遇到并发问题,则表明在BatchRunnable
迭代它时会对其进行修改。
用ArrayList
替换CopyOnWriteArrayList
只会隐藏并发问题。
在您的代码中,您在创建BatchRunnable时修改了列表clear()
和add()
。当第一个runnable被提交时,它会开始处理列表,但您仍然会继续修改它。