【问题标题】:Java HashMap ConcurrentModification Exception despite using synchronized block尽管使用了同步块,Java HashMap ConcurrentModification 异常
【发布时间】:2020-03-31 18:51:26
【问题描述】:

我有一个哈希图同时在多个线程中使用。为了使其线程安全,我将其放入同步块中:

private final Map<Long, DeviceConnection> mapConnections = new HashMap()<>;

...

synchronized (mapConnections) {
        List<Long> toClear = new ArrayList<>();
        for (Map.Entry<Long, AndroidSocketConnection> entry : mapConnections.entrySet()) {
            if (entry.getValue().isReadyToRemove())) {
                removed++;
                toClear.add(entry.getKey());
            }
        }
        for(Long toC : toClear) {
            mapConnections.remove(toC);
        }
    }

我想如果我把它放到同步块中我不必关心这些东西,但是抛出了这个异常:

java.util.ConcurrentModificationException

在 java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)

在 java.util.HashMap$EntryIterator.next(HashMap.java:1476)

在 java.util.HashMap$EntryIterator.next(HashMap.java:1474)

at myPackage.network.DeviceHandler.doClearing(DeviceHandler.java:51) // -> 这一行包含我展示的代码的for循环头 p>

在 java.lang.Thread.run(Thread.java:748)

【问题讨论】:

  • 是否还有其他代码在 mapConnections 上运行?
  • 是的。这就是我使用同步块的原因。我一直在其他线程中从这张地图中放置和删除东西
  • 其他线程是否也通过synchronized (mapConnections) 块访问?
  • 不,但是我在这个 HashSet 中引用了对象,我在其中更改了不在同步块中的属性,这可能是个问题吗?
  • 这解释了ConcurrentModificationException。至于在没有同步的情况下对其他对象进行操作,是的,这绝对是一个问题。它不会导致ConcurrentModificationException,但您会遇到难以理解且难以重现的错误。

标签: java multithreading concurrency hashmap concurrentmodification


【解决方案1】:

只有在每次对映射的访问(包括读取和写入)都通过synchronized 块执行时,它才会是线程安全的。

ConcurrentModificationException 将在地图被修改而被迭代时被抛出。

我建议您切换到ConcurrentHashMap,它是线程安全的,可以直接替代。

【讨论】:

  • 听起来不错,但我也将此 HashSet 上的所有其他读取内容放入同步块中。
  • 但是我引用了这个 HashSet 中的对象,我在其中更改了不在同步块中的属性,这可能是个问题吗?
  • @julien-100000 这些更改的属性会影响hashCode 方法的结果吗?那会很糟糕。
  • 不,如果您更改项目键的 hashCode 将不起作用。您必须先删除该条目,然后进行更改并添加更新版本。 stackoverflow.com/questions/26959545/… 但是你的密钥是Long,所以它可能不会受到这个问题的影响。如果您更改其中一个值的 hashCode 无关紧要,只需更改键即可。
  • @julien-100000 What's the implementation of hashCode in java Object?。结果:当依赖于hashCode 时,例如用作基于哈希的映射中的键,您应该使用对象内的稳定值(例如Employee 类的员工ID)来实现equalshashCode ,或 Product 类的产品 ID 字段。 “稳定”意味着该值在您的应用程序运行时不会改变,或者至少在基于哈希的使用期间不会改变,例如作为地图中的键。
猜你喜欢
  • 2010-12-11
  • 2014-01-24
  • 2014-08-05
  • 2018-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
相关资源
最近更新 更多