总结 HashTable, HashMap, ConcurrentHashMap 之间的区别
前言
HashMap 本身不是线程安全的.
在多线程环境下使用哈希表可以使用:
- Hashtable(不推荐使用)
- ConcurrentHashMap(推荐使用)
HashMap

HashMap数据结构
根本: 数组 + 链表(jdk1.7)/数组+链表+红黑树(jdk1.8)(当链表长度超过阈值(8)将链表转为红黑树 时间复杂度降低为O(logn))
基本概念
容量(capacity ): 默认16 一个桶中的容量
加载因子(load factor): 默认0.75 即桶中的可利用大小
HashMap时间复杂度
若美好的状态下没有hash冲突 每个桶只有一个元素时间复杂度 O(1) ,最差是O(n) 红黑树则是O(logn)。
为什么HashMap非线程安全
1 put()时,若两个线程都put了同样的key,则值会被覆盖
(已被修复) 当A线程put()数据时都发现空间不够,执行resize()时,而同时B线程也put()数据也发现空间不够执行resize(),有可能在A线程rehash()生成新表时节点i->k,而B线程rehash()生成新表时又将节点k->j,导致生成了死循环(i.next=k;k.next=i;)当一旦进入这个链表,就会导致死循环。
扩容造成死循环和数据丢失
假设现在有两个线程A、B同时对下面这个HashMap进行扩容操作
正常扩容后的结果是下面这样的:
但是当线程A执行到上面transfer函数的第11行代码时,CPU时间片耗尽,线程A被挂起。即如下图中位置所示:
此时线程A中:e=3、next=7、e.next=null
当线程A的时间片耗尽后,CPU开始执行线程B,并在线程B中成功的完成了数据迁移
重点来了,根据Java内存模式可知,线程B执行完数据迁移后,此时主内存中newTable和table都是最新的,也就是说:7.next=3、3.next=null。
随后线程A获得CPU时间片继续执行newTable[i] = e,将3放入新数组对应的位置,执行完此轮循环后线程A的情况如下:
接着继续执行下一轮循环,此时e=7,从主内存中读取e.next时发现主内存中7.next=3,此时next=3,并将7采用头插法的方式放入新数组中,并继续执行完此轮循环,结果如下:
此时没任何问题。
上轮next=3,e=3,执行下一次循环可以发现,3.next=null,所以此轮循环将会是最后一轮循环。
接下来当执行完e.next=newTable[i]即3.next=7后,3和7之间就相互连接了,当执行完newTable[i]=e后,3被头插法重新插入到链表中,执行结果如下图所示:
上面说了此时e.next=null即next=null,当执行完e=null后,将不会进行下一轮循环。到此线程A、B的扩容操作完成,很明显当线程A执行完后,HashMap中出现了环形结构,当在以后对该HashMap进行操作时会出现死循环。
并且从上图可以发现,元素5在扩容期间被莫名的丢失了,这就发生了数据丢失的问题。
其中第六行代码是判断是否出现hash碰撞,假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完第六行代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。
除此之前,还有就是代码的第38行处有个++size,我们这样想,还是线程A、B,这两个线程同时进行put操作时,假设当前HashMap的zise大小为10,当线程A执行到第38行代码时,从主内存中获得size的值为10后准备进行+1操作,但是由于时间片耗尽只好让出CPU,线程B快乐的拿到CPU还是从主内存中拿到size的值10进行+1操作,完成了put操作并将size=11写回主内存,然后线程A再次拿到CPU并继续执行(此时size的值仍为10),当执行完put操作后,还是将size=11写回内存,此时,线程A、B都执行了一次put操作,但是size的值只增加了1,所有说还是由于数据覆盖又导致了线程不安全。
具体
- 首先HashMap本身线程不安全
- 其次HashMap的key值可以为空
下面是HashMap的hash方法,可以看出如果key值为空,hash方法的返回值为0
- HashMap 扩容是等某一次 put 后发现负载因子不达标后开始扩容, 新建一个表, 重新将元素哈希(具体细节有机会再讲)
HashTable
- Hashtable源码中的put方法中的key如果为空,hashCode方法会抛出空指针异常

2.1**HashTable的线程安全只是简单的把关键方法加上了 synchronized 关键字. **

- 相当于直接针对 Hashtable 对象本身加锁.
- 如果多线程访问同一个 Hashtable 就会直接造成锁冲突.
- size 属性也是通过 synchronized 来控制同步, 也是比较慢的.
- 一旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到大量的元素拷贝, 效率会非常低.
- 一个Hashtable只有一把锁,两个线程访问Hashtable中的任意数据都会出现锁竞争,就如上图,有两个线程要操作这两个元素,由于是一把大锁,就会产生竞争,但是仔细分析,这两操作在不同的哈希桶上,不牵扯修改同一个变量,因此就不会发生线程安全,所以上面的锁竞争是没有必要的。
- 如果两个修改落到同一个哈希桶上,有线程安全风险
ConcurrentHashMap
相比之下ConcurrentHashMap就做出了重大的改进,把锁的粒度细化了
- 读操作没有加锁(但是使用了 volatile 保证从内存读取结果), 只对写操作进行加锁. 加锁的方式仍然 是是用 synchronized, 但是不是锁整个对象, 而是 “锁桶” (用每个链表的头结点作为锁对象), 大大降 低了锁冲突的概率.
- 充分利用 CAS 特性. 比如 size 属性通过 CAS 来更新. 避免出现重量级锁的情况.
- 优化了扩容方式: 化整为零
「
发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去.
扩容期间, 新老数组同时存在.
后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程. 每个操作负责搬运一小 部分元素.
搬完最后一个元素再把老数组删掉.
这个期间, 插入只往新数组加.
这个期间, 查找需要同时查新数组和老数组」
此时一个哈希表上面的桶的个数可能会非常多,导致出现锁冲突的概率,大大降低了
扩容=申请更大的内存+搬运元素(搬运元素过程肯定要重新哈希)
上面讲的都是基于Java 8里的;在Java1.7里,ConcurrentHashMap不是每个桶一个锁,而是“分段锁”一个锁管若干个桶
- ConcurrenHashMap的key也不可以为空,看下面的源码中的put方法,如果key==null则会抛出空指针异常
总结
- HashMap: 线程不安全. key 允许为 null
- Hashtable: 线程安全. 使用 synchronized 锁 Hashtable 对象, 就是一把大锁,锁冲突极高,效率较低. key 不允许为 null.
- ConcurrentHashMap: 线程安全. 使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用 CAS 机制. 优化了扩容方式. key 不允许为 null
相关文章:
总结 HashTable, HashMap, ConcurrentHashMap 之间的区别
前言 HashMap 本身不是线程安全的. 在多线程环境下使用哈希表可以使用: Hashtable(不推荐使用)ConcurrentHashMap(推荐使用) HashMap HashMap数据结构 根本: 数组 链表(jdk1.7)/数组链表红黑…...
《剑指 Offer》专项突破版 - 面试题 107 : 矩阵中的距离(C++ 实现)
题目链接:矩阵中的距离 题目: 输入一个由 0、1 组成的矩阵 M,请输出一个大小相同的矩阵 D,矩阵 D 中的每个格子是矩阵 M 中对应格子离最近的 0 的距离。水平或竖直方向相邻的两个格子的距离为 1。假设矩阵 M 中至少有一个 0。 …...
揭秘智慧礼品背后的故事
如若不是从事技术行业,在罗列礼品清单时,可能不会想到 “数据”,但幸运的是,我们想到了。如何将AI技术应用到当季一些最受青睐的产品中去,训练数据是这一智能技术的背后动力。很多电子设备或名称中带有“智能”一词的设…...
NVM的安装与配置
目录 一、简介二、下载2.1、windows环境下载地址2.2、安装 三、配置3.1、查看可安装版本3.2、安装版本3.3、使用和切换版本3.4、模块配置 四、其他4.1、全局安装pnpm4.2、常用nvm命令 一、简介 NVM,全称为Node Version Manager,是一个流行的命令行工具&a…...
[Java EE] 多线程(一) :线程的创建与常用方法(上)
1. 认识线程 1.1 概念 1.1.1 什么是线程 ⼀个线程就是⼀个"执⾏流".每个线程之间都可以按照顺序执⾏⾃⼰的代码.多个线程之间"同时"执⾏ 着多份代码. 还是回到我们之前的银⾏的例⼦中。之前我们主要描述的是个⼈业务,即⼀个⼈完全处理⾃⼰的…...
Linux安装docker(含Centos系统和Ubuntu系统)
一、Centos系统 1. 卸载旧版本依赖 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 2. 设置仓库 安装所需的软件包。yum-utils 提供了 yum-config-manager &…...
【第十五届蓝桥杯大赛软件赛省赛】———— C/C++ 大学B组
蓝桥杯2024年15届省赛b组原题献上...
Redis+lua脚本限制ip多次输入错误密码
Redislua脚本限制ip多次输入错误密码 不能锁username,因为如果有人恶意保留破解密码的话。会导致用户本人无法登录。 这里我采用 以ip的方式进行锁定。利用redis 设置key:ip。value:当前ip尝试登录的次数 实现逻辑 逻辑简单,假设…...
全球顶级的低代码开发平台,你知道几个?
什么是低代码开发平台? 低码开发平台是一个应用程序,提供图形用户界面编程,从而以非常快的速度开发代码,减少了传统的编程工作。 这些工具有助于快速开发代码,最大限度地减少手工编码的努力。这些平台不仅有助于编码,而且还能快速安装和部署。 低码开发工具的好处 低代码平…...
11-1.Vue2.x基本列表—v-for
文章目录 Vue2.x基本列表—v-for Vue2.x基本列表—v-for <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>基本列表</title><script type"text/javascript" src"../js/vue.j…...
一本书精通推荐算法,轻松搞定入门、面试、进阶
当前互联网高速发展,用户规模和内容规模均迅猛提升。 身处信息严重过载的时代,如何让用户从海量信息中发现自己感兴趣的内容,成了很多公司的核心问题。 在此背景下,搜索系统和推荐系统应运而生。 前者主要解决用户主动寻找内容…...
ADB的基本语法及常用命令
学习网址 ADB命令的基本语法如下: adb [-d|-e|-s <serialNumber>] <command> 如果有多个设备/模拟器连接,则需要为命令指定目标设备。 参数及含义如下: 常用命令如下: 1. 启动ADB服务 adb start-server 2. 停止…...
Linux之bpfjit(2)使用分析和mini-tcpdump实现
Linux之bpfjit(2)使用分析和mini-tcpdump实现 Author: Once Day Date: 2024年4月13日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可以参考专栏:…...
adb常用命令汇总
Android Debug Bridge (adb) 是一个多功能命令行工具,它允许你与连接的Android设备或在电脑上的Android模拟器进行通信。下面列出了一些常用的adb命令: 启动adb服务: adb start-server停止adb服务: adb kill-server查看已连接的设…...
JVM虚拟机(三)垃圾回收简介、垃圾回收算法、分代回收、垃圾回收器种类、G1垃圾回收器
目录 一、什么是垃圾回收?1.1 什么是垃圾回收?1.2 什么对象能被垃圾回收?1)引用计数法2)可达性分析算法 二、JVM 垃圾回收算法2.1 标记清除算法2.2 标记整理算法(标记压缩算法)2.3 复制算法2.4 …...
JavaScript基础:js介绍、变量、数据类型以及类型转换
目录 介绍 引入方式 内部方式 外部形式 注释和结束符 单行注释 多行注释 结束符 输入和输出 输出 输入 变量 声明 赋值 关键字 变量名命名规则 常量 数据类型 数值类型 字符串类型 布尔类型 undefined 类型转换 隐式转换 显式转换 Number ✨介绍 &a…...
【牛客SQL快速入门】SQL基础(三)
一、条件函数 IF 条件函数 IF函数是最常用到的条件函数,写法为 if(xn,a,b),xn代表判断条件,如果xn时,那么结果返回a,否则返回b。 -- 把非北京大学的用户统一归为其他大学 Select device_id,if(university ‘北京大…...
Pytorch手撸Attention
Pytorch手撸Attention 注释写的很详细了,对照着公式比较下更好理解,可以参考一下知乎的文章 注意力机制 import torch import torch.nn as nn import torch.nn.functional as Fclass SelfAttention(nn.Module):def __init__(self, embed_size):super(S…...
PyCharm 2024.1 发布:全面升级,助力高效编程!
PyCharm 2024.1 发布:全面升级,助力高效编程! 文章目录 PyCharm 2024.1 发布:全面升级,助力高效编程!摘要引言 Hugging Face:模型和数据集的快速文档预览针对 JavaScript 和 TypeScript 的全行代…...
Nginx基础(06)
Nginx基础(05) uWSGI 介绍 uWSGI 是一个 Web服务器 主要用途是将Web应用程序部署到生产环境中 可以用来连接Nginx服务与Python动态网站 1. 用 uWSGI 部署 Python 网站项目 配置 Nginx 使其可以将动态访问转交给 uWSGI 安装 python 工具及依赖 安…...
量子行走:从理论到Python实现——6. 量子机器学习与前沿应用
量子机器学习探索了量子计算与人工智能的交叉领域,通过利用量子叠加与纠缠特性处理经典难以应对的高维数据模式。Berkeley CS269Q课程与PennyLane教程系统阐述了从量子特征映射到实际化学模拟的完整技术栈,本章将围绕特征空间扩展、优化求解与信息安全三…...
Qwen3-14B-AWQ模型效果深度评测:在算法题求解上的表现
Qwen3-14B-AWQ模型效果深度评测:在算法题求解上的表现 1. 评测背景与模型简介 在AI技术快速发展的今天,大语言模型在代码生成和算法解题领域展现出越来越强的能力。Qwen3-14B-Int4-AWQ作为通义千问系列的最新量化版本,在保持较高推理能力的…...
Android架构组件
Android架构组件:构建现代化应用的利器 在移动应用开发中,良好的架构设计是保证应用稳定性和可维护性的关键。Google推出的Android架构组件(Android Architecture Components)为开发者提供了一套标准化工具,帮助简化开…...
【算法对抗】打穿查重黑盒!论文降AI太难?8个实测有效策略与高性价比工具
上周匆匆写完论文初稿交给导师,结果被一眼识破,当场打回。还被导师认为不认真不负责态度不端正! 为了搞定这件事,我测评了市面上大部分的主流工具、试了无数方法,终于把AI率降到6%。 我们要先端正态度:论文…...
Beyond Compare 5 三步快速激活方案:从评估错误到专业版授权的完整指南
Beyond Compare 5 三步快速激活方案:从评估错误到专业版授权的完整指南 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen Beyond Compare 5 作为业界领先的文件比对与合并工具…...
开源工具实现游戏存档编辑:虚幻引擎存档处理全指南
开源工具实现游戏存档编辑:虚幻引擎存档处理全指南 【免费下载链接】uesave 项目地址: https://gitcode.com/gh_mirrors/ue/uesave 在游戏开发与玩家体验中,虚幻引擎的存档文件往往以二进制格式存储,这给数据修改、备份与分析带来了挑…...
不止是收发数据:挖掘常兴串口调试助手V5.01的5个隐藏效率神器(自动回复/进制转换/批量发送)
挖掘常兴串口调试助手V5.01的5个隐藏效率神器 在嵌入式开发领域,串口调试工具早已超越了简单的数据收发功能。常兴串口调试助手V5.01作为一款专业级工具,集成了多项提升开发效率的实用功能。本文将深入解析五个常被忽视但极具价值的隐藏功能,…...
1Panel新手必看:5分钟搞定RustDesk远程桌面搭建(含端口配置避坑指南)
1Panel极速部署RustDesk:零基础构建安全远程桌面的完整指南 当我们需要远程管理Linux服务器时,一个轻量级、开源的远程桌面解决方案往往比商业软件更灵活可控。RustDesk作为新兴的远程工具,凭借其跨平台特性和自建服务器的能力,正…...
20吨燃气蒸汽锅炉实力厂家/支持上门安装调试
燃气蒸汽锅炉,认准源头实力厂家,不仅能买到品质过硬的设备,更能享受到省心便捷的上门安装调试服务,免去自行安装的繁琐与隐患,让设备快速投入平稳运行。我们作为深耕锅炉制造行业的实力厂家,具备正规生产资…...
OpenClaw安全防护配置:Qwen3.5-9B任务执行边界与权限控制
OpenClaw安全防护配置:Qwen3.5-9B任务执行边界与权限控制 1. 为什么需要安全防护? 当我第一次在本地部署OpenClaw时,最让我不安的是这个AI助手拥有和我一样的系统权限。它能读写我的文件、发送邮件、甚至执行终端命令——这种能力就像把家门…...













