我正在使用多线程来批量处理字符串列表,但是当Runnable任务迭代List以处理每个字符串时,我收到此错误。
例如,代码大致遵循以下结构:
public class RunnableTask implements Runnable {
private List<String> batch;
RunnableTask(List<String> batch){
this.batch = batch;
}
@Override
public void run() {
for(String record : batch){
entry = record.split(",");
m = regex.matcher(entry[7]);
if (m.find() && m.group(3) != null){
currentKey = m.group(3).trim();
currentValue = Integer.parseInt(entry[4]);
if ( resultMap.get(currentKey) == null ){
resultMap.put(currentKey, currentValue);
} else {
resultMap.put(currentKey, resultMap.get(currentKey) + currentValue);
}
}
}
}
}
通过这些批处理进行处理的线程永远不会修改“批处理”,并且在for循环内不会批量处理批处理。我知道这个异常ConcurrentModificationException是由于在迭代期间修改了List,但据我所知,没有发生。有什么我想念的吗?
感谢任何帮助,
三江源!
UPDATE1:似乎实例变量不是线程安全的。我试图使用CopyOnWriteArrayList代替ArrayList但是我收到了不一致的结果 - 表明在以某种方式修改列表并且不是每个元素都被处理之前,完整的迭代没有完成。
UPDATE2:使用sychronized和/或reentrantlock锁定循环仍然会产生相同的异常。
我需要一种方法将列表传递给Runnable任务,并在没有新线程的情况下迭代这些列表,从而导致该列表出现并发问题。
答案 0 :(得分:2)
我理解这个异常ConcurrentModificationException是由于在迭代过程中修改了List,但据我所知,没有发生
好的,请考虑创建新线程时发生的情况,将引用传递给RunnableTask
实例,使用不同的列表初始化为构造函数参数?您刚刚更改了列表引用以指向不同的列表。并且考虑一下,当run()
方法中的不同线程在任何时候改变list
时会发生什么。这将在某个时间点抛出ConcurrentModificationException
。
实例变量不是线程安全 。
答案 1 :(得分:0)
在您的代码中尝试此操作:
public void run() {
for(String record : new ArrayList(batch)){
//do processing with record
}
}
处理列表的所有线程都有一个问题(在进程中修改的列表是什么?)但很难用你提供的代码判断
答案 2 :(得分:0)
问题是由于多个线程同时修改源List
结构。我建议你应该将源列表分配给新的子列表(根据大小)并将该列表传递给线程。
说你的来源List
有100个元素。并且您正在运行5个并发线程。
int index = 0;
List<TObject> tempList = new ArrayList<>();
for(TObject obj:srcList){
if(i==(srcList.size()/numberOfthread)){
RunnableTask task = new RunnableTask(tempList);
tempList = new ArrayList<>();
}else
tempList.add(obj);
}
在这种情况下,您的原始列表不会被修改。
答案 3 :(得分:0)
您需要在访问其元素之前锁定列表。因为List不是线程安全的。试试这个
public void run() {
synchronizd(batch){
for(String record : batch){//do processing with record}
}
}
答案 4 :(得分:0)
是的,你得到了ConcurrentModificationException,因为你的List在迭代期间被修改了。如果性能不是关键问题,我建议使用同步。 公共类RunnableTask实现Runnable {
private List<String> batch = new ArrayList<String>();
RunnableTask(List<String> batch){
this.batch = batch;
}
public void run() {
synchronized (batch) {
for(String record : batch){//do processing with record}
}
}
}
}
甚至更好地使用ReentrantLock。
答案 5 :(得分:0)
您的后续操作表明您尝试多次重复使用相同的List。您的调用者必须为每个Runnable创建一个新列表。
答案 6 :(得分:0)
显然,其他人正在更改列表的内容,这与您提到的代码不相符。 ( 如果您确定ConcurrentModificationException正在抱怨batch
列表,而不是resultMap
,并且您实际上是在RunnableTask中显示所有代码 )
尝试搜索您的代码,对于正在更新列表内容的地方,请检查是否可以与RunnableTask
同时进行。
只需在RunnableTask中进行同步就没有用,你需要同步对列表的所有访问,这显然发生在其他地方。
如果性能对您来说是一个问题,那么您无法在batch
列表上同步(禁止多个RunnableTask
同时执行),请考虑使用ReaderWriterLock:RunnableTask获取读锁定,而列表更新逻辑获取写锁定。