ConcurrentHashMap 和 Hashtable 的区别【表格对比】【详细】【重点】

本文详细对比了ConcurrentHashMap与Hashtable的底层数据结构、线程安全实现、锁粒度及扩容策略等内容。从JDK1.7到JDK1.8,ConcurrentHashMap经历了从分段锁到Node数组+链表/红黑树的变化,而Hashtable则始终使用整体锁,导致效率较低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ConcurrentHashMap

Hashtable

底层数据结构

  • JDK1.7的 ConcurrentHashMap 底层采用 Segment数组 + HashEntry数组 实现;
  • JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树

直接就是HashMap加上Synchronized

实现线程安全的方式

  • 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。)
  • JDK1.8 的时候已经摒弃了Segment的概念,而是直接用Node 数组+链表+红黑树的数据结构来实现,并发控制使用synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本

Hashtable(整张表一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

锁粒度

  • JDK1.7:每个Segment数组(继承自ReentrantLock)一把锁(默认16个);读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值;
  • JDK1.8:每个Node一把锁;

一共一把锁

扩容

  • JDk1.7:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容;
  • JDK1.8:(参考JavaGuide大神)
    • 所在链表的元素个数达到了阈值 8,则会调用treeifyBin 方法把链表转换成红黑树,不过在结构转换之前,会对数组长度进行判断;
    • 如果数组长度n小于阈值MIN_TREEIFY_CAPACITY ,默认是64,则会调用tryPresize 方法把数组扩大到原来的两倍,并触发transfer 方法,重新调整节点的位置;
    • 新增节点之后,会调用addCount 方法记录元素个数,并检查是否需要进行扩容,当数组元素个数达到阈值时,会触发transfer 方法,重新调整节点的位置;

同HashMap的扩容方式,每次2倍,因子是0.75

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加油当当

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值