Java 迭代 ArrayList 时出现 ConcurrentModificationException
Java ConcurrentModificationException when iterating ArrayList
在迭代 ArrayList
并将对象添加到辅助 ArrayList
时,我得到 ConcurrentModificationException
。我真的不知道为什么,因为我没有编辑我正在迭代的列表。
这发生在我的代码的两个部分。这些是代码。
编辑 - 代码 1:
public static ConcurrentHashMap<Long, ArrayList<HistoricalIndex>> historicalIndexesMap = new ConcurrentHashMap<Long, ArrayList<HistoricalIndex>>();
ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);
List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : historicalIndexList){ //EXCEPTION HERE
if((System.currentTimeMillis()-hi.getTimestamp()) >= ONE_MINUTE){
tmpList.add(hi.getIndex());
}
}
在上面的代码 1 中,我是否应该像这样复制 historicalIndexList:
ArrayList<HistoricalIndex> historicalIndexList = new ArrayList<HistoricalIndex>(IndexService.historicalIndexesMap.get(id));
而不是这样做:?
ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);
代码 2:
List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : list){ //EXCEPTION HERE
tmpList.add(hi.getIndex());
}
有人知道为什么会这样吗?
堆栈跟踪:
21:19:50,426 ERROR [stderr] (pool-9-thread-6) java.util.ConcurrentModificationException
21:19:50,429 ERROR [stderr] (pool-9-thread-6) at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
21:19:50,432 ERROR [stderr] (pool-9-thread-6) at java.util.ArrayList$Itr.next(ArrayList.java:831
那是因为您试图同时访问它,ArrayList
而不是 synchronized
。
您可以使用同步的 java.util.Vector
或使 ArrayList
同步执行:
Collections.synchronizedList(new ArrayList(...));
正如@izca 评论的那样,为避免并发修改,您应该将创建的列表放在同步块中:
List<T> myList = Collections.synchronizedList(new ArrayList<T>(...));
synchronized(myList) {
// to modify elements in myList
}
让我们简单了解一下导致ConcurrentModificationException 的原因。 ArrayList 在内部维护一个修改计数值,它只是一个整数,每次对列表进行修改时都会递增。创建迭代器时,它采用此值的 'snapshot'。然后,每次使用迭代器时,它都会检查该值的副本是否仍然与数组自己的副本相匹配。如果没有,则抛出异常。
这意味着要发生 ConcurrentModificationException,必须在您首次创建迭代器之后(即在首次执行 for() 语句之后但在结束之前)对 ArrayList 进行一些修改。由于您的 for() 循环没有修改 ArrayList,这意味着在您遍历数组时一定有其他线程正在更改数组。
编辑:在回复您的编辑时,是的,如果其他线程要更改它,您应该复制该数组。你甚至可以这样做:
for(HistoricalIndex hi: new ArrayList<HistoricalIndex>(historicalIndexList))
...开始循环时复制列表。
综上所述,ConcurrentModificationException 与我们通常认为的并发问题无关。通过修改迭代器循环内的数组,而不是通过迭代器的 remove() 方法,您可以很容易地在单个线程中获得一个。 'Concurrent' 在这种情况下意味着迭代和修改同时发生 - 无论是在相同还是不同的线程中。
在迭代 ArrayList
并将对象添加到辅助 ArrayList
时,我得到 ConcurrentModificationException
。我真的不知道为什么,因为我没有编辑我正在迭代的列表。
这发生在我的代码的两个部分。这些是代码。
编辑 - 代码 1:
public static ConcurrentHashMap<Long, ArrayList<HistoricalIndex>> historicalIndexesMap = new ConcurrentHashMap<Long, ArrayList<HistoricalIndex>>();
ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);
List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : historicalIndexList){ //EXCEPTION HERE
if((System.currentTimeMillis()-hi.getTimestamp()) >= ONE_MINUTE){
tmpList.add(hi.getIndex());
}
}
在上面的代码 1 中,我是否应该像这样复制 historicalIndexList:
ArrayList<HistoricalIndex> historicalIndexList = new ArrayList<HistoricalIndex>(IndexService.historicalIndexesMap.get(id));
而不是这样做:?
ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);
代码 2:
List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : list){ //EXCEPTION HERE
tmpList.add(hi.getIndex());
}
有人知道为什么会这样吗?
堆栈跟踪:
21:19:50,426 ERROR [stderr] (pool-9-thread-6) java.util.ConcurrentModificationException
21:19:50,429 ERROR [stderr] (pool-9-thread-6) at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
21:19:50,432 ERROR [stderr] (pool-9-thread-6) at java.util.ArrayList$Itr.next(ArrayList.java:831
那是因为您试图同时访问它,ArrayList
而不是 synchronized
。
您可以使用同步的 java.util.Vector
或使 ArrayList
同步执行:
Collections.synchronizedList(new ArrayList(...));
正如@izca 评论的那样,为避免并发修改,您应该将创建的列表放在同步块中:
List<T> myList = Collections.synchronizedList(new ArrayList<T>(...));
synchronized(myList) {
// to modify elements in myList
}
让我们简单了解一下导致ConcurrentModificationException 的原因。 ArrayList 在内部维护一个修改计数值,它只是一个整数,每次对列表进行修改时都会递增。创建迭代器时,它采用此值的 'snapshot'。然后,每次使用迭代器时,它都会检查该值的副本是否仍然与数组自己的副本相匹配。如果没有,则抛出异常。
这意味着要发生 ConcurrentModificationException,必须在您首次创建迭代器之后(即在首次执行 for() 语句之后但在结束之前)对 ArrayList 进行一些修改。由于您的 for() 循环没有修改 ArrayList,这意味着在您遍历数组时一定有其他线程正在更改数组。
编辑:在回复您的编辑时,是的,如果其他线程要更改它,您应该复制该数组。你甚至可以这样做:
for(HistoricalIndex hi: new ArrayList<HistoricalIndex>(historicalIndexList))
...开始循环时复制列表。
综上所述,ConcurrentModificationException 与我们通常认为的并发问题无关。通过修改迭代器循环内的数组,而不是通过迭代器的 remove() 方法,您可以很容易地在单个线程中获得一个。 'Concurrent' 在这种情况下意味着迭代和修改同时发生 - 无论是在相同还是不同的线程中。