英文:
ConcurrentModificationException when iterate through ImmutableMap.copyof()
问题
目前,我在频繁遍历一个HashMap时遇到了问题(每秒1次)。
我在主线程中向地图添加新元素,然后在另一个线程中遍历地图。
我返回一个ImmutableMap.copyOf()来进行遍历,有时我会向地图中添加新元素。但是它会抛出ConcurrentModificationException异常。
java.util.ConcurrentModificationException: null
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911) ~[?:1.8.0_261]
at java.util.ArrayList$Itr.next(ArrayList.java:861) ~[?:1.8.0_261]
我的伪代码:
class Foo {
private final Map<String, List<User>> userMap = new HashMap<>();
public static void addUser(String houseId, User user) {
if(!userMap.containsKey(houseId)) {
userMap.put(houseId, new ArrayList<>());
}
List<User> list = userMap.get(houseId);
list.add(user);
}
public static Map<String, List<User>> getAll() {
return ImmutableMap.copyOf(userMap);
}
}
// 遍历任务
// 每秒运行一次的任务
class Task extends TimerTask {
public void run() {
for (List<User> listUser : Foo.getAll().values()) {
for (User user : listUser) {
user.sayHello(); // 打印 hello
}
}
}
}
我认为使用ImmutableMap.copyOf()会防止我遇到这个问题。因为我读到过在从其他线程中遍历时应该使用列表/映射的不可变副本。
我认为我通过使用CopyOnWriteArrayList来“解决”了这个问题。但我真的很好奇为什么会抛出错误。
谢谢!!!
英文:
Currently, I have a problem when I frequently iterating through a HashMap (1-time per second)
I add new element to the map on the main thread and iterate the map on the other thread
I return an ImmutableMap.copyOf() to iterate through it, and sometimes I add a new element to the map. But it throws me a ConcurrentModificationException
java.util.ConcurrentModificationException: null
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911) ~[?:1.8.0_261]
at java.util.ArrayList$Itr.next(ArrayList.java:861) ~[?:1.8.0_261]
My pseudo-code:
<!-- language: java -->
class Foo {
private final Map<String, List<User>> userMap = new HashMap<>();
public static void addUser(String houseId, User user) {
if(!userMap.containsKey(houseId)) {
userMap.put(houseId, new ArrayList<>());
}
List<User> list = userMap.get(houseId);
list.add(user);
}
public static Map<String, List<User>> getAll() {
return ImmutableMap.copyOf(userMap);
}
}
// iterating task
// this task run one per second
class Task extends TimerTask {
public void run() {
for (List<User> listUser : Foo.getAll().values()) {
for (User user : listUser) {
user.sayHello(); // print hello
}
}
}
}
<!-- language: java -->
I thought that use an ImmutableMap.copyOf() will prevent me from this problem. Because I read that you should use an immutable copy of your list/map when iterating through it from other thread.
I think I "solve" this by using CopyOnWriteArrayList. But I'm really curious why it throws me an error.
Thank you!!!
答案1
得分: 1
正如您在堆栈跟踪中所看到的,错误发生在ArrayList中,而不是Map中。
在addUser
方法中,通过list.add(user);
将一个元素添加到列表中。
这个操作可能会在另一个线程使用for (User user : listUser)
遍历元素时发生,从而导致错误。
当您使用CopyOnWriteArrayList替换ArrayList时,list.add(user)
在内部进行复制,并将新用户添加到副本中。因此,正在运行的迭代不受影响,因为它遍历旧版本的数组。因此,正在运行的迭代将忽略新值。任何新的迭代器将包括新值。
英文:
As you can see in the stack trace, the error occurs in the ArrayList, not the Map.
In method addUser
, an element is added to the list via
list.add(user);
This operation might happen, while another thread loops over the elements with for (User user : listUser)
causing the error.
When you exchange the ArrayList with a CopyOnWriteArrayList, the list.add(user)
internally makes a copy and adds the new user into the copy. Therefore, a potentially running iteration is not affected since it loops over the old version of the array. So the running iteration will ignore the new value. Any new iterator will include the new value.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论