我正在经历一个面试问题on JavaRevisited,我很难理解这个问题:
在多线程环境中使用HashMap有什么问题?当get()方法进入无限循环时?
在我看来,在多线程环境中使用HashMap
并不是问题,只要我们的应用程序不访问/读取正在修改创建的HashMap
的线程,而不是简单访问HashMap。
所以,正如我所看到的那样,只要在应用程序中我们只是在多线程环境中访问HashMap
就没有问题。
如果我的理解是正确的,请告诉我。
答案 0 :(得分:21)
在多线程环境中使用HashMap有什么问题?当get()方法转到无限循环?
有什么问题是让多个线程以不受保护的方式使用非同步集合(实际上是任何可变类)。确定如果每个线程都有自己的HashMap
实例,那么这不是问题。如果多个线程添加到相同的 HashMap
实例而不是synchronized
,则 是一个问题。即使只有一个线程正在修改HashMap
而其他线程正在从同一个地图读取而没有同步,您将遇到问题。
如果您需要在多个线程中使用相同的哈希表对象,那么您应该考虑使用ConcurrentHashMap
,将每个访问包含在HashMap
块中的synchronized {}
,或者制作使用Collections.synchronizedMap(new HashMap<...>())
构造。
get()
进入无限循环,因为其中一个线程只有内存中HashMap
的部分更新的视图,并且必须有某种指针循环。这是使用具有多个线程的非同步集合的危险。
所以在我的理解中,只要在应用程序中我们只是在多线程环境中访问HashMap,这不是问题吗?
如果通过“访问”表示“阅读”,则确实具有资格。你必须确保:
HashMap
(HashMap
或迭代而不删除)如果这些条件中的任何一个不正确,那么您将需要使用同步地图。
答案 1 :(得分:1)
这是一个经典问题。 ArrayList和HashMap不同步,而Vector和HashTable是。因此,除非您自己非常谨慎地定义互斥锁,否则应该使用HashTable。
换句话说,例如, HashTable将确保在任何给定时间没有其他线程正在使用HashTable。如果您使用HashMap,则必须通过确保在调用方法之前在HashMap上进行同步来手动执行此操作。
更新:结账@Gray的评论。看起来用Collections.synchronizedMap(新的HashMap())包装HashMap是现在的方法。
编辑:其他海报的回答方式比我做得好。然而,我的回答引起了关于即将被弃用的Vector,Stack,Hashtable和Dictionary类的使用的有趣讨论,所以我在这里留下问题,作为下面评论的头。谢谢你们!答案 2 :(得分:1)
我们知道HashMap
是一个非同步的集合,而其同步的对应部分是HashTable
。因此,当您在多线程环境中访问集合时,并且所有线程都在访问集合的单个实例时,出于各种显而易见的原因,例如,使用HashTable
会更安全。以避免脏读并保持数据一致性。在最坏的情况下,这种多线程环境也会导致无限循环。
是的,这是真的。 HashMap.get()
可能导致无限循环。让我们看看如何?
如果您查看源代码HashMap.get(Object key)
的方法,则如下所示:
public Object get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry e = table[i];
while (true) {
if (e == null)
return e;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
while(true){...}
在运行时始终可能成为无限循环的受害者。 多线程环境IF,e.next可以通过某种方式指向自身。这将导致无限循环。但是,e.next将如何指向自身?
这可以在void transfer(Entry[] newTable)
方法中发生,该方法在HashMap调整大小时调用。
do {
Entry next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e
= next;
} while (e != null);
如果调整大小,并且其他线程试图修改地图实例,则这段代码很容易产生上述条件。
避免这种情况的唯一方法是在代码中使用同步,或者更好的方法是使用同步的集合。
答案 3 :(得分:0)
我猜他们打算访问HashMap
的共享副本。 Shared mutable state
。
由于它不是synchronized
,每个线程都会从主内存中获取副本,修改并覆盖它。
HashMap with one entry <n, 1>
thread 1 grab the copy
thread 2 grab the copy
thread 1 modify <n, 2>
thread 2 modify <n, 3>
thread 1 is done, and stores the copy in the main memory
now memory is <n, 2>
thread 2 is done and stores the copy
now memory is <n, 3>
The state thread 1 is lost