ConcurrentHashMap JDK1.8 源码

本文详细解析了JDK1.8中ConcurrentHashMap的get、put操作过程,包括初始化、碰撞处理、扩容以及在并发环境下的线程安全性。对比了JDK1.8以前版本的实现,阐述了锁分离技术,并指出在1.8版本中仍然使用synchronized关键字确保部分操作的线程安全。

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

ConcurrentHashMap JDK1.8 源码 get put操作过程:

put:
检查key或value是否为空,如果key或value为空,则抛异常,反之进入for死循环,
    检查table是否初始化了,如果没有,则进行初始化(初始化完成后进行下一次循环),如果已经初始化则根据key找到hash表中索引位置table[i],
        如果没有发生碰撞,则利用CAS操作直接存储在table[i],如果成功则退出循环,反之进行下一次循环
        如果发生碰撞,检查table[i]的节点的hash是否等于MOVED(-1),
           如果等于,则检测到正在扩容,则帮助其扩容(扩容完成后进行下一次循环)
           (如果发生碰撞)如果没扩容,用synchronized锁定该索引位置的结点,(table[i],hash值相同的链表的头节点)
             如果是链表节点,key存在则修改,key不存在则插入链表尾部,如果链表结点数大于等于8则转化为红黑树
             如果是红黑树节点,key存在则修改,key不存在则插入树中         
get:
根据key计算在table出现的位置i.
如果table为空或者table[i]为空,返回空,
反之如果table[i]的头结点的key满足条件,是则返回头结点的value;否则分别根据树、链表查询。 

以上是原创,以下是转载自https://blog.youkuaiyun.com/u010412719/article/details/52145145

 

ConcurrentHashMap 在JDK1.8版本以前的实现原理

既然本篇博文的标题明确的标出了是基于JDK1.8版本的,也就暗示了这个版本和以前的版本关于ConcurrentHashMap有些许的不同,对吧。

下面我们就先借助网上的资料来看下以前版本的ConcurrentHashMap的实现思路。

我们都知道HashMap是线程不安全的。Hashtable是线程安全的。看过Hashtable源码的我们都知道Hashtable的线程安全是采用在每个方法来添加了synchronized关键字来修饰,即Hashtable是针对整个table的锁定,这样就导致HashTable容器在竞争激烈的并发环境下表现出效率低下。

效率低下的原因说的更详细点:是因为所有访问HashTable的线程都必须竞争同一把锁。当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

基于Hashtable的缺点,人们就开始思考,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率呢??这就是我们的“锁分离”技术,这也是ConcurrentHashMap实现的基础。

ConcurrentHashMap使用的就是锁分段技术,ConcurrentHashMap由多个Segment组成(Segment下包含很多Node,也就是我们的键值对了),每个Segment都有把锁来实现线程安全,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

因此,关于ConcurrentHashMap就转化为了对Segment的研究。这是因为,ConcurrentHashMap的get、put操作是直接委托给Segment的get、put方法,但是自己上手上的JDK1.8的具体实现确不想网上这些博文所介绍的。因此,就有了本篇博文的介绍。

推荐几个JDK1.8以前版本的关于ConcurrentHashMap的原理分析,方便大家比较。

1、http://www.iteye.com/topic/344876

2、http://ifeve.com/concurrenthashmap/

如需要更多,请自己网上搜索即可。

下面就开始JDK1.8版本中ConcurrentHashMap的介绍。

 

JDK1.8 版本中ConcurrentHashMap介绍

1、前言

首先要说明的几点:

1、JDK1.8的ConcurrentHashMap中Segment虽保留,但已经简化属性,仅仅是为了兼容旧版本。

2、ConcurrentHashMap的底层与Java1.8的HashMap有相通之处,底层依然由“数组”+链表+红黑树来实现的,底层结构存放的是TreeBin对象,而不是TreeNode对象;

3、ConcurrentHashMap实现中借用了较多的CAS算法,unsafe.compareAndSwapInt(this, valueOffset, expect, update); CAS(Compare And Swap),意思是如果valueOffset位置包含的值与expect值相同,则更新valueOffset位置的值为update,并返回true,否则不更新,返回false。

ConcurrentHashMap既然借助了CAS来实现非阻塞的无锁实现线程安全,那么是不是就没有用锁了呢??答案:还是使用了synchronized关键字进行同步了的,在哪里使用了呢?在put操作hash值相同的链表的头结点还是会synchronized上锁,这样才能保证线程安全。

看完ConcurrentHashMap整个类的源码,给自己的感觉就是和HashMap的实现基本一模一样,当有修改操作时借助了synchronized来对table[i]进行锁定保证了线程安全以及使用了CAS来保证原子性操作,其它的基本一致,例如:ConcurrentHashMap的get(int key)方法的实现思路为:根据key的hash值找到其在table所对应的位置i,然后在table[i]位置所存储的链表(或者是树)进行查找是否有键为key的节点,如果有,则返回节点对应的value,否则返回null。思路是不是很熟悉,是不是和HashMap中该方法的思路一样。所以,如果你也在看ConcurrentHashMap的源码,不要害怕,思路还是原来的思路,只是多了些许东西罢了。

 

https://blog.youkuaiyun.com/u010723709/article/details/48007881

https://blog.youkuaiyun.com/u010412719/article/details/52145145

https://www.jianshu.com/p/d10256f0ebea

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值