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、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