Java 并发:当并发线程只移除元素时,HashMap 与 ConcurrentHashMap

Java Concurrency: HashMap Vs ConcurrentHashMap when concurrent threads only remove elements

我有一个创建 HashMap 的主线程,向其添加多个可运行对象,然后启动每个可运行对象(将 HashMap 传递给每个对象)。 runnable 在即将完成处理之前从映射中删除它的对象。

我想知道在这种情况下是否有任何理由使用 ConcurrentHashMap(而不是 HashMap)- 可运行对象在映射上执行的唯一操作是将它们自己从映射中删除。在这种情况下是否存在需要使用 ConcurrentHashMap 的并发性考虑?

主线程

private final Map<Integer, Consumer> runnableMap = new HashMap<>();

Runnable runnable;

for (int i = 1; i <= NUM_RUNNABLES; i++) {
    runnable = new Consumer(i, runnableMap);
    runnableMap.put(i, runnable);
    executionContext.execute(runnable);
}

消费者实现 Runnable

private final Integer consumerNumber;
private final Map<Integer, Consumer> runnableMap;

public Consumer(int consumerNumber, final Map<Integer, Consumer> runnableMap){
    this.consumerNumber = consumerNumber;
    this.runnableMap = runnableMap;
}

public void run() {
    :::
    // business logic
    :::
    // Below remove is the only operation this thread executes on the map
    runnableMap.remove(consumerNumber);
}

如果您这样做的原因是为了跟踪线程完成情况,为什么不使用 CountdownLatch?不确定 HashMap 是否只能在删除时出现并发问题,我建议仅当您的代码不会因任何可能的问题而中断时才使用它,或者使用 ConcurrentHashMap。

您询问了 HashMapConcurrentHashMap 之间的差异,但还有一个额外的数据结构需要考虑:Hashtable。每个都有差异和权衡。您需要评估哪种最适​​合您的预期用途。

  • HashMap 是不同步的,所以如果有多个线程可以读取或写入它,您的结果将不可预测。 HashMap 也允许 null 作为键或值。

  • Hashtable 是同步的,不支持空键或空值。来自 the Hashtable Javadoc:

    Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.

  • ConcurrentHashMap 是线程安全的,不允许 null 用作键或值。

javadoc of HashMap 说:

Note that this implementation is not synchronized.

If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map.

如前所述,删除是结构性的改变,必须使用同步

另外,在Hashmap的removeNode()方法中(remove()方法调用),modCount变量自增,负责ConcurrentModificationException .所以如果你在没有同步的情况下删除元素,你可能会得到这个异常。

因此您必须使用 ConcurrentHashMap