[JavaSE-01]ConcurrentHashMap和HashMap的详解

本文详细比较了HashMap和ConcurrentHashMap在数据结构、线程安全性和版本更新上的差异,重点讲解了ConcurrentHashMap如何通过Segment和锁机制解决多线程问题,以及在JDK1.8中的优化变化。

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

ConcurrentHashMap和HashMap的区别

1. hashMap概述

HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。

其次,HashMap是非线程安全的,只是用于单线程环境下,多线程环境下应该采用concurrent并发包下的concurrentHashMap。

HashMap 实现了Serializable接口,因此支持序列化,实现了Cloneable接口,能被克隆。

HashMap中key和value都允许为null。key为null的键值对永远都放在以table[0]为头结点的链表中。

1.7及以前的HashMap结构

img

1.8 后的HashMap结构

img

2. ConcurrentHashMap概述

​ HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。

​ HashMap虽然处理数据的效率高,但是在多线程的情况下存在着安全问题,因此设计了CurrentHashMap来解决多线程安全问题。

1.7及以前的ConcurrentHashMap结构

​ 在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,主要实现原理是实现了锁分离的思路解决了多线程的安全问题,如下图所示:

image-20231121222553106

​ Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。

此时的 ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:

put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Segment里面的Entry,然后在遍历entry链表.

put操作
 static class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) { this.loadFactor = lf; }
    }

​ 从上Segment的继承体系可以看出, Segment实现了ReentrantLock,也就带有锁的功能。
当执行put操作时,会进行第一次key的hash来定位Segment的位置,如果该Segment还没有初始化,即通过CAS操作进行赋值,然后进行第二次hash操作,找到相应的HashEntry的位置,这里会利用继承过来的锁的特性,在将数据插入指定的HashEntry位置时(链表的尾端),会通过继承ReentrantLock的tryLock()方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋的方式去继续的调用tryLock()方法去获取锁,超过指定次数就挂起,等待唤醒。

get操作

​ ConcurrentHashMap的get操作跟HashMap类似,只是ConcurrentHashMap第一次需要经过一次hash定位到Segment的位置,然后再hash定位到指定的HashEntry,遍历该HashEntry下的链表进行对比,成功就返回,不成功就返回null

1.8 的ConcurrentHashMap结构

​ JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本.
img

img

总结区别点:

  1. HashMap线程不安全。
  2. ConcurrentHashMap线程安全。
  3. ConcurrentHashMapJava7中使用segment实现,对每个segment加锁。
    ap`线程安全。
  4. ConcurrentHashMapJava7中使用segment实现,对每个segment加锁。
  5. ConcurrentHashMapJava8中是直接在HashMap的基础上进行加锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值