Java CopyOnWriteArrayList迭代与多线程不一致?
我目前正在用ExecutorService发送CopyOnWrite ArrayList中要并行处理的成批字符串,其中处理这些列表的可运行任务需要遍历列表并对每个字符串进行处理 在遇到与常规ArrayList的并发性问题后,我尝试使用CopyOnWriteArrayList,因为它们是线程安全的,但是我的结果现在不一致。也就是说,我在每次运行程序时都会得到不同的结果,这表明在每个可运行TAK可以完全迭代arraylist之前,arraylist的内容会以某种方式发生更改Java CopyOnWriteArrayList迭代与多线程不一致?,java,multithreading,concurrency,iteration,Java,Multithreading,Concurrency,Iteration,我目前正在用ExecutorService发送CopyOnWrite ArrayList中要并行处理的成批字符串,其中处理这些列表的可运行任务需要遍历列表并对每个字符串进行处理 在遇到与常规ArrayList的并发性问题后,我尝试使用CopyOnWriteArrayList,因为它们是线程安全的,但是我的结果现在不一致。也就是说,我在每次运行程序时都会得到不同的结果,这表明在每个可运行TAK可以完全迭代arraylist之前,arraylist的内容会以某种方式发生更改 public stati
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
}
}
我猜您使用迭代器遍历元素。构造迭代器时,迭代器提供列表状态的快照。遍历迭代器时不需要同步
因此,在您的情况下,您应该在构造函数中获取迭代器,或者复制
CopyOnWriteArrayList
查看while
循环:
while((line = br.readLine()) != null) {
recordBatch.add(line);
if(recordBatch.size() == 100){
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch.clear();
}
}
在所有的BatchRunnable
中传递对相同列表的引用。因此,只要您在一个位置更改列表
,它就会反映在所有引用中。因此,使用recordBatch.clear()
清除列表后,所有引用的列表都是空的,即使是在BatchRunnable
中的引用。这就是为什么你会得到不一致的结果
您应该在BatchRunnable
中传递recordBatch
列表的copy
:
worker = new BatchRunnable(new ArrayList<String>(recordBatch));
worker=newbatchrunable(newarraylist(recordBatch));
如果使用普通的ArrayList
时遇到并发问题,这表明当您的BatchRunnable
在其上迭代时,它被修改了。
将ArrayList
替换为CopyOnWriteArrayList
只会隐藏并发问题
在代码中,您正在修改(clear()
和add()
列表,同时创建batch runnable。当提交第一个runnable时,它开始处理列表,但您仍在继续修改它。将批传递给batch runnable
后,您正在清除该批
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>();
worker=newbatchrunnable(recordBatch);
执行人,执行人(工人);
recordBatch=新建CopyOnWriteArrayList();
如果在迭代批之前完全构建批(即没有其他线程在更改内容)那么我希望你能接受ArrayList。这表明你的批处理发生了意外的变化。你应该关注这一方面。我添加了构建批处理的方式,是否有理由相信这会导致问题?旁注:你应该使用阻塞队列,而不是这正是他们想要做的…@howlerdingdong。检查我的编辑。你需要传递一份你的列表。将你在if
中的第一句话更改为我在末尾发布的那句话。成功!谢谢。我没有意识到我传递的引用可能会引起问题!
worker = new BatchRunnable(recordBatch);
executor.execute(worker);
recordBatch = new CopyOnWriteArrayList<String>();