我有一个主线程,它创建一个HashMap,向其中添加多个可运行对象,然后启动每个runnable(将HashMap传递给每个)。runnable在即将完成处理之前从映射中删除其对象。
我想知道在这种情况下是否有任何理由使用并发哈希图(而不是哈希图)——可运行对象在地图上执行的唯一操作是从地图中删除自己。在这种情况下,是否有并发考虑需要使用并发哈希图?
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);
}
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是否仅在删除时才会出现并发问题,我建议仅在您的代码不会在任何可能的问题上中断时使用它,或者使用并发HashMap。
HashMap
的javadoc说:
请注意,此实现不是同步的。
如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部同步。(结构修改是任何添加或删除一个或多个映射的操作;仅仅更改与实例已经包含的键关联的值不是结构修改。)这通常是通过在自然封装映射的某个对象上进行同步来实现的。
如上所述,删除是一种结构更改,您必须使用同步。
此外,在Hashmap的RemoveNode()
方法(由delete()
方法调用)中,modCount
变量递增,它负责ContranstModificationException
。因此,如果您在不同步的情况下删除元素,您可能会遇到此异常。
因此,您必须使用ConCurrentHashMap
。
您询问了HashMap
和ConCurrentHashMap
之间的差异,但是还有一个额外的数据结构需要考虑:Hashtable
。每个都有差异和权衡。您需要评估哪个最适合您的预期用法。
>
HashMap
是未同步的,因此如果多个线程可以读取或写入它,您的结果将是不可预测的。HashMap
还允许null作为键或值。
Hashtable
是同步的,不支持空键或值。来自Hashtable Javadoc:
哈希表是同步的。如果不需要线程安全的实现,建议使用HashMap代替Hashtable。如果需要一个线程安全的高并发实现,那么建议使用并发HashMap代替Hashtable。
并发HashMap
是线程安全的,不允许将null用作键或值。