我们为什么要在多线程环境中使用HashMap?

时间:2013-07-27 11:48:59

标签: java hashmap synchronized

今天我正在阅读HashMap如何在java中运行。我遇到了一个博客,我直接引用了博客的文章。我已经阅读了有关Stackoverflow的this文章。仍然 我想知道细节。

  

所以答案是肯定存在潜在的竞争条件   在Java中调整HashMap的大小,如果两个线程同时发现的话   现在HashMap需要调整大小,他们都试图调整大小。在...上   在Java中调整HashMap大小的过程,即bucket中的元素   存储在链表中在迁移期间按顺序颠倒   到新的存储桶,因为java HashMap没有附加新元素   尾部而是在头部附加新元素以避免尾部穿越。   如果发生竞争条件,那么你将最终得到一个无限循环。

它指出,由于HashMap在调整HashMap的大小调整期间不是线程安全的 情况可能发生。我甚至在办公室项目中看到过,人们都在广泛使用 HashMaps知道它们不是线程安全的。如果它不是线程安全的,我们为什么要使用HashMap 然后?是开发人员缺乏知识,因为他们可能不了解ConcurrentHashMap等结构或其他原因。任何人都可以解释这个难题。

6 个答案:

答案 0 :(得分:8)

我可以自信地说ConcurrentHashMap是一个非常被忽略的类。没有多少人知道它,也没有多少人关心它。该类提供了一种非常强大且快速的同步Map集合的方法。我在Web上阅读了一些HashMap和ConcurrentHashMap的比较。我只想说他们完全错了。你无法比较两者,一个提供访问地图的同步方法,而另一个提供无法同步。

我们大多数人都没有注意到的是,虽然我们的应用程序,尤其是Web应用程序,在开发过程中工作正常。在测试阶段,它们通常在重载(或甚至中等重载)下倾斜。这是因为我们希望我们的HashMap能够以某种方式运行,但是在负载下它们通常是行为不端的。 Hashtable提供对其条目的并发访问,只需要一个小小的警告,整个映射被锁定以执行任何类型的操作。

虽然这种开销在正常负载下的Web应用程序中是可忽略的,但在负载很重的情况下,它可能会导致响应时间延迟和服务器过载,这是没有充分理由的。这就是ConcurrentHashMap的步骤。它们提供了Hashtable的所有功能,其性能几乎与HashMap一样好。 ConcurrentHashMap通过一种非常简单的机制实现了这一目标。

默认情况下,集合维护一个包含16个锁的列表,而不是映射范围锁,每个锁用于保护(或锁定)地图的单个存储桶。这实际上意味着16个线程可以一次修改集合(只要它们都在不同的桶上工作)。事实上,此集合没有锁定整个地图的操作。

答案 1 :(得分:2)

这有几个方面:首先,大多数集合都不是线程安全的。如果您想要线程安全集合,可以致电synchronizedCollectionsynchronizedMap

但重点是:你希望你的线程并行运行,根本不需要同步 - 如果可能的话。这是你应该努力的事情,但当然每次处理多线程时都无法实现。 但是,使默认的集合/映射线程安全是没有意义的,因为它应该是共享映射的边缘情况。同步意味着jvm的更多工作。

答案 2 :(得分:1)

我做了一些研究,我会说所有答案都很好。但我们显然可以 看看为什么在HashMap中出现竞争条件。经过对stackoverflow的一些研究后,我发现了这些参考资料,他们非常值得进一步研究这个概念。

  1. Why a race condition occurs in HashMap
  2. Is Concurrent Hashmap better then Hashmap
  3. Multi-threaded environment while doing resizing
  4. 我想他们已经澄清了我的概念。

答案 3 :(得分:0)

在多线程环境中,您必须确保它不会同时修改,或者您可以解决关键内存问题,因为它不会以任何方式同步。

亲爱的,先检查一下Api,我也是以同样的方式思考。

我认为解决方案是使用静态Collections.synchronizedMap方法。我期待它能够返回更好的实现。但是如果你看一下源代码,你会发现他们所做的只是一个包含在互斥锁上同步调用的包装器,它恰好是同一个映射,不允许读取同时发生。

在Jakarta commons项目中,有一个名为FastHashMap的实现。此实现具有称为快速的属性。如果fast为true,则读取是非同步的,写入将执行以下步骤:

Clone the current structure
Perform the modification on the clone
Replace the existing structure with the modified clone 
public class FastSynchronizedMap implements Map,   
Serializable {

private final Map m;
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

.
.
.

public V get(Object key) {
lock.readLock().lock();
V value = null;
try {
    value = m.get(key);
} finally {
    lock.readLock().unlock();
}
return value;
}

public V put(K key, V value) {
lock.writeLock().lock();
V v = null;
try {
    v = m.put(key, value);
} finally {
    lock.writeLock().lock();
}
return v;
}

.
.
.
}

请注意,我们尝试finally块,我们希望保证无论块中遇到什么问题都会释放锁。

当您几乎没有写操作,并且主要是读操作时,此实现很有效。

答案 4 :(得分:0)

当单个线程有权访问时,可以使用Hashmap。但是,当多个线程开始访问Hashmap时,会出现两个主要问题: 1.调整hashmap的大小不能保证按预期工作。 2.将抛出并发修改异常。当单个线程访问它同时读取和写入hashmap时,也可以抛出它。

答案 5 :(得分:0)

在多线程环境中使用HashMap的一种解决方法是使用预期数量的对象初始化它。计数,因此无需重新调整大小。