02 java难点源码阅读

集合源码之ConcurrentHashMap

2016-12-20  本文已影响109人  jatesun

引子

上篇解析过的hashmap不是线程安全的,因此并发大师Doug Lea在jdk1.5增加了concurrenthashmap类以满足开发者在多线程环境安全使用map的需要。concurrenthashmap作为多线程环境常使用的类,它是如何实现线程安全的?但是又为什么说它不能完全替代hashtable?弱一致性又是怎样一回事?读完这篇解析concurrenthashmap的文章,我相信你会对concurrenthashmap的方方面面有一定的了解。
涉及到源码的分析,一般我们都是先分析数据结构,然后在分析算法,数据结结构和算法都理解了,那么你就了解了大部分内容。类似上篇hashmap,对concurrenthashmap的分析也分为三个部分:

为了简单,跟hashmap类似的部分本文将一笔带过,如果没看过hashmap源码可以看上篇文章集合源码之hashmap,hashmap是concurrenthashmap的基础,不建议没读过hashmap直接看本篇文章。本文分析的源码采用的是concurrenthashmap较为简单的jdk1.6版本。

concurrentHashMap的数据结构

首先我们来看concurrenthashmap数据结构的图示,有个整体的认识。



图里的是主要组成表示,我们更加具体的去分析concurrenthashmap的数据结构要分为concurrenthashmap的数据结构、segment的数据结构以及hashEntry的数据结构。重要的数据结构我们会解析用途以便大家更好的理解算法实现部分。

常量

实例变量

segment

我们有必要对segment单独的进行数据结构分析,因为主要的算法实现都是在segment中,segment是concurrenthashmap的灵魂。可以把segment看做是线程安全的hashmap,因为数据结构与hashmap非常相似。

concurrenthashmap重要算法实现

正如上文提到的,concurrenthashmap的灵魂是segment,segment的算法实现是整个concurrenthashmap的基础,我们会着重分析segment的实现。

构造方法

多个构造方法,只讨论最核心的构造方法。主要分为下面几个步骤:

        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = 1;
        while (cap < c)
            cap <<= 1;

        for (int i = 0; i < this.segments.length; ++i)
            this.segments[i] = new Segment<K,V>(cap, loadFactor);

这段代码计算cap的最终目的是为了确认初始化segment大小,让cap与segment总数的乘积要大于等于初始容量的大小。

重要算法

需要说明的是除了全局的算法,大部分算法都是确定是哪个segment,然后调用segment方法。分为四类探讨增删改查,另外不接受null的value。

增(put(key,value)/putIfAbsent(key,value))

增加方法也是调用segment的put(key,hash,value,boolean onlyIfAbsent)。onlyifabsent如果为true,则如果segment存在key不替换value。put默认替换value。put方法先lock,然后进行put操作。put方法跟hashmap类似,此处不赘述。

删(remove(key)/remove(key,value))

删除方法也是调用segment的remove(key,hash,value)方法。先找到要删除的节点,找到后删除节点,但是这里的删除跟hashmap中的有所不同,因为hashentry的next是final的不可改变,所以只能新建节点,next指向删除节点的next,然后克隆删除节点前继节点,这样就完成了删除操作。
另外clear方法是遍历segment,调用segment的clear方法。clear方法加锁,遍历hashentry数组置null。

修改方法有两个,一个是replace(key,value)调用segment的replace(key,hash,value),另一个是replace(key,oldvalue,newvalue)调用segment的replace(key,hash,oldvalue,newvalue)。
- segment---replace(key, hash, value):先加锁,然后遍历节点找到对应的节点,替换value。如果不存在key对应的节点就返回null。
- segment---replace(key, hash,oldvalue,newvalue):跟上面的方法类似,不同的是如果节点里的value跟传入的oldvalue不同也不进行替换。

深入探讨

concurrenthashmap的弱一致性

什么叫弱一致性?理想的强一致性就是放入一个元素后,立即获取元素就是刚放入的元素,但是concurrenthashmap可能某时间段看不到这个元素。如下图两个线程执行顺序:

则刚放入的元素可能会取不到。详细的可以参考文章为什么ConcurrentHashMap是弱一致的

与hashtable、collections封装后的SynchronizedMap比较

其实还可以加入concurrenthashmap的segment。

上一篇 下一篇

猜你喜欢

热点阅读