java基础之HashMap导致死循环以及ConcurrentHashMap和HashTable
HashMap多线程操作可能导致死循环问题
主要原因在于并发下的Rehash会造成元素之间形成一个循环链表。不过jdk1.8后解决了这个问题,但还是不建议在多线程下使用HashMap,因为多线程下使用HashMap还是会存在其他问题比如数据丢失。并发环境下推荐使用ConcurrentHashMap。
ConcurrentHashMap和Hashtable的区别
主要体现在实现线程安全的方式不同:
-
底层数据结构:JDK1.7的ConcurrentHashMap底层采用分段数组+链表 实现,JDK1.8采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树 。Hashtable和JDK1.8之前的HashMap的底层数据结构都是采用数组+链表的形式,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
-
实现线程安全的方式:① 在JDK1.7 的时候ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高了并发访问率 ,到了JDK1.8 的时候就已经摒弃了segment的概念,而是直接用Node数组+链表+红黑树的结构来实现,并发控制使用synchronized和CAS来操作(JDK1.6以后对synchronized锁做了很多优化 )整个看起来像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁 ):使用synchronized来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询 状态,如果使用put 添加元素,另一个线程就不能使用put 添加元素,也不能用get ,竞争越激烈效率越低。
ConcurrentHashMap线程安全的具体实现方式/底层具体实现
JDK1.7如上图
首先将数据分成一段一段的存储,然后给每段数据分配一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问。
ConcurrentHashMap是由Segment数组结构和HashEnty数组结构组成
Segment实现了ReetrantLock,所以Sement是一种可重入锁,扮演锁的角色。HashEntry用于存储键值对数据。
一个ConurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构,一个Segment包含HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须先获得对应的Segment的锁。
JDK1.8如上图
ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java8在链表长度超过一定阈值(8 )时将链表转换成红黑树
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律