前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官脑子有病系列:为什么 HashMap 是线程不安全的?

面试官脑子有病系列:为什么 HashMap 是线程不安全的?

原创
作者头像
王二蛋
发布2024-06-19 21:52:06
1120
发布2024-06-19 21:52:06

前言

Hi,大家好,我是王二蛋。

我们在面试的时候,经常会被问到一些有的没的、看似高深但与日常工作关系不大的问题。也因此被大家调侃为“面试造火箭,工作拧螺丝”。

今天就来探讨一个在Java面试时经常被问到的一个问题:为什么 HashMap 是线程不安全的?

HashMap为啥线程不安全?

估计HashMap听到这个问题都会骂一句:

HashMap 内心OS:我一个数据结构为啥要线程安全?

不过他既然喜欢问,咱就答给他看。

对HashMap不了解的小伙伴,先简单了解一下:HashMap本身并不具备线程安全,如果在多线程的情况下没有采取同步措施,直接对其进行修改操作,就可能会引发线程安全问题。

接下来看看,到底是什么情况下会出现线程安全问题。

HashMap线程不安全的根本原因

HashMap 的不安全主要是内部的修改不是原子操作。主要涉及以下几个操作:

  1. put 方法中的非原子性操作。
  2. 扩容时的非原子性操作。

put 方法中的非原子性操作

在 HashMap 的 put 方法中,会涉及到多个步骤,包括计算键的哈希值、找到对应的桶、处理哈希冲突等。这些步骤在没有外部同步的情况下不是原子的,所以在多线程并发时可能会出现问题。

假设有两个线程 A 和 B 同时尝试 put 同一个键到 HashMap 中:

  1. 线程 A 计算了哈希值并找到了对应的桶。
  2. 线程 B 在线程 A 还未完成 put 操作时,也计算了相同的哈希值并尝试修改同一个桶。
  3. 如果线程 A 在线程 B 修改桶之后才完成 put 操作,那么线程 B 的修改可能会被线程 A 的操作覆盖,从而导致数据丢失。

扩容时的非原子性操作

当 HashMap 中的元素数量超过阈值时,会触发扩容操作。扩容涉及到以下几个步骤:

  1. 创建一个新的数组。
  2. 重新计算所有键的哈希值。
  3. 将原数组中的元素迁移到新数组中。

这个过程也是非原子的,所以在多线程并发时也可能会出现问题。

假设有两个线程 A 和 B:

  • 线程 A 检测到 HashMap 需要扩容,并开始创建新的数组。
  • 线程 B 在线程 A 完成扩容之前,向 HashMap 中添加了一个新的元素。
  • 线程 A 完成扩容后,可能没有考虑到线程 B 添加的新元素,从而导致数据丢失。

安全的HashMap

尽管HashMap存在线程安全问题,但在非并发修改的场景中,它依然是性能最优的键值对存储选择。

当然,如果确保线程安全,我们有其他选项。比如使用ConcurrentHashMap或通过Collections.synchronizedMap将普通HashMap转化为线程安全的Map。

总结

HashMap开发时使用率是非常高的,面试官问这个问题主要是考察对数据结构理解、对线程安全的掌握以及有没有高并发项目的经验。所以,在平时的工作中要多思考、多观察,做到知其然知其所以然。

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • HashMap为啥线程不安全?
  • HashMap线程不安全的根本原因
    • put 方法中的非原子性操作
      • 扩容时的非原子性操作
      • 安全的HashMap
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档