今天我正在阅读HashMap如何在java中运行。我遇到了一个博客,我直接引用了博客的文章。我已经阅读了有关Stackoverflow的this文章。仍然 我想知道细节。
所以答案是肯定存在潜在的竞争条件 在Java中调整HashMap的大小,如果两个线程同时发现的话 现在HashMap需要调整大小,他们都试图调整大小。在...上 在Java中调整HashMap大小的过程,即bucket中的元素 存储在链表中在迁移期间按顺序颠倒 到新的存储桶,因为java HashMap没有附加新元素 尾部而是在头部附加新元素以避免尾部穿越。 如果发生竞争条件,那么你将最终得到一个无限循环。
它指出,由于HashMap在调整HashMap的大小调整期间不是线程安全的 情况可能发生。我甚至在办公室项目中看到过,人们都在广泛使用 HashMaps知道它们不是线程安全的。如果它不是线程安全的,我们为什么要使用HashMap 然后?是开发人员缺乏知识,因为他们可能不了解ConcurrentHashMap等结构或其他原因。任何人都可以解释这个难题。
答案 0 :(得分:8)
我可以自信地说ConcurrentHashMap是一个非常被忽略的类。没有多少人知道它,也没有多少人关心它。该类提供了一种非常强大且快速的同步Map集合的方法。我在Web上阅读了一些HashMap和ConcurrentHashMap的比较。我只想说他们完全错了。你无法比较两者,一个提供访问地图的同步方法,而另一个提供无法同步。
我们大多数人都没有注意到的是,虽然我们的应用程序,尤其是Web应用程序,在开发过程中工作正常。在测试阶段,它们通常在重载(或甚至中等重载)下倾斜。这是因为我们希望我们的HashMap能够以某种方式运行,但是在负载下它们通常是行为不端的。 Hashtable提供对其条目的并发访问,只需要一个小小的警告,整个映射被锁定以执行任何类型的操作。
虽然这种开销在正常负载下的Web应用程序中是可忽略的,但在负载很重的情况下,它可能会导致响应时间延迟和服务器过载,这是没有充分理由的。这就是ConcurrentHashMap的步骤。它们提供了Hashtable的所有功能,其性能几乎与HashMap一样好。 ConcurrentHashMap通过一种非常简单的机制实现了这一目标。
默认情况下,集合维护一个包含16个锁的列表,而不是映射范围锁,每个锁用于保护(或锁定)地图的单个存储桶。这实际上意味着16个线程可以一次修改集合(只要它们都在不同的桶上工作)。事实上,此集合没有锁定整个地图的操作。
答案 1 :(得分:2)
这有几个方面:首先,大多数集合都不是线程安全的。如果您想要线程安全集合,可以致电synchronizedCollection
或synchronizedMap
但重点是:你希望你的线程并行运行,根本不需要同步 - 如果可能的话。这是你应该努力的事情,但当然每次处理多线程时都无法实现。 但是,使默认的集合/映射线程安全是没有意义的,因为它应该是共享映射的边缘情况。同步意味着jvm的更多工作。
答案 2 :(得分:1)
我做了一些研究,我会说所有答案都很好。但我们显然可以 看看为什么在HashMap中出现竞争条件。经过对stackoverflow的一些研究后,我发现了这些参考资料,他们非常值得进一步研究这个概念。
答案 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的一种解决方法是使用预期数量的对象初始化它。计数,因此无需重新调整大小。