在对ImmutableMap.copyof()进行迭代时出现ConcurrentModificationException。

huangapple go评论76阅读模式
英文:

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&lt;String, List&lt;User&gt;&gt; userMap = new HashMap&lt;&gt;();

	public static void addUser(String houseId, User user) {
		if(!userMap.containsKey(houseId)) {
			userMap.put(houseId, new ArrayList&lt;&gt;());
		}
		List&lt;User&gt; list = userMap.get(houseId);
		list.add(user);
	}

	public static Map&lt;String, List&lt;User&gt;&gt; getAll() {
		return ImmutableMap.copyOf(userMap);
	}
}

// iterating task
// this task run one per second

class Task extends TimerTask {
	public void run() {
		for (List&lt;User&gt; 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.

huangapple
  • 本文由 发表于 2020年9月28日 00:31:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/64090786.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定