首页 > 编程知识 正文

为什么说 HashMap 是非线程安全的?

时间:2023-05-06 10:14:15 阅读:100513 作者:3338

点击Java编程技术天堂,轻松关注!及时获取有趣且信息量大的技术文章。

做一个积极向上的人。

编码,修复bug,提升自己。

我有一个乐园,它面向编程,开满了春花!

0.HashMap简单说几句

当我们研究HashMap时,我们都知道HashMap是非线程安全的,我们也知道HashTable是线程安全的,因为其中的方法是同步的。

但是为什么HashMap是非线程安全的呢?仅仅是因为内部方法没有用同步的关键字修饰吗?本文主要分析原因。

我们知道HashMap的底层是一个Entry数组。当发生哈希冲突时,通过链表来解决HashMap,链表的头节点存储在对应的数组位置。对于链表,新添加的节点将从第一个节点开始添加。

HashMap为什么线程不安全,在什么情况下多个线程并发时可能会出现问题?

Javadoc中关于hashmap的一段描述如下:

这个实现是不同步的。如果多个线程同时访问一个哈希映射,并且其中至少有一个线程在结构上修改了该映射,那么它必须保持外部同步。(结构修改是指添加或删除一个或多个映射关系的任何操作;仅更改与实例中已包含的键相关联的值不是结构修改。)这通常通过同步自然封装映射的对象来完成。如果使用Collections.synchronizedMap方法来“包装”地图。最好在创建时这样做,以防止对映射的意外异步访问,如下所示:

map map=collections . synchronized map(新HashMap());

1.插入时的哈希映射

上面的addEntry方法将在Hashmap进行put操作时调用。

现在,如果线程A和线程B同时对同一个数组位置调用addEntry,两个线程会同时得到当前的头节点,然后A写入新的头节点后,B也写入新的头节点,那么B的写操作会覆盖A的写操作,导致A的写操作丢失。

2.当Hashmap扩展其容量时

添加新的键值对后,当键值对的总数超过阈值时,AddEntry将调用调整大小操作。代码如下:

此操作将生成新容量的新数组,然后重新计算原始数组的所有键值对并写入新数组,然后指向新生成的数组。

HashMap有一个扩展操作,它将生成一个新容量的新数组,然后重新计算原始数组的所有键值对并将其写入新数组,然后指向新生成的数组。

然后,问题来了。当多个线程同时进入并检测到总数超过阈值时,将同时调用调整大小操作。每个线程将生成一个新数组,并在重新散列后将其分配给地图底部的数组。因此,只有最后一个线程生成的新数组将被分配到映射的底部,所有其他线程都将丢失。而且,当一些线程已经完成赋值,而其他线程刚刚开始时,它们会使用分配的表作为原始数组,这也会导致问题。

其他地方可能存在很多线程安全问题,我就不一一列举了。简而言之,HashMap是非线程安全的,当出现并发问题时,建议使用ConcrrentHashMap。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。