在多线程环境中使用HashMap

时间:2012-06-15 12:20:54

标签: java multithreading

我正在经历一个面试问题on JavaRevisited,我很难理解这个问题:

  

在多线程环境中使用HashMap有什么问题?当get()方法进入无限循环时?

在我看来,在多线程环境中使用HashMap并不是问题,只要我们的应用程序不访问/读取正在修改创建的HashMap的线程,而不是简单访问HashMap。

所以,正如我所看到的那样,只要在应用程序中我们只是在多线程环境中访问HashMap就没有问题。

如果我的理解是正确的,请告诉我。

4 个答案:

答案 0 :(得分:21)

  

在多线程环境中使用HashMap有什么问题?当get()方法转到无限循环?

有什么问题是让多个线程以不受保护的方式使用非同步集合(实际上是任何可变类)。确定如果每个线程都有自己的HashMap 实例,那么这不是问题。如果多个线程添加到相同的 HashMap实例而不是synchronized,则 是一个问题。即使只有一个线程正在修改HashMap而其他线程正在从同一个地图读取而没有同步,您将遇到问题。

如果您需要在多个线程中使用相同的哈希表对象,那么您应该考虑使用ConcurrentHashMap,将每个访问包含在HashMap块中的synchronized {},或者制作使用Collections.synchronizedMap(new HashMap<...>())构造。

get()进入无限循环,因为其中一个线程只有内存中HashMap部分更新的视图,并且必须有某种指针循环。这是使用具有多个线程的非同步集合的危险。

  

所以在我的理解中,只要在应用程序中我们只是在多线程环境中访问HashMap,这不是问题吗?

如果通过“访问”表示“阅读”,则确实具有资格。你必须确保:

  • {/ 1}}的所有更新在线程实例化之前完成,并且创建地图的线程也会分叉线程
  • 线程仅以只读模式使用HashMapHashMap或迭代而不删除)
  • 没有线程更新地图

如果这些条件中的任何一个不正确,那么您将需要使用同步地图。

答案 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 &amp;&amp; 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