concurrenthashmap

jdk7 concurrenthashmap
segment数组,默认大小,最大大小
segment中有成员变量hashentry数组,元素个数,阀值,modcount,默认大小
hashtable 增加的是对象实例锁
concurrenthashmap加的是分段锁,是以segment为单位。segment继承了ReentrantLock。
segment 最大的数量是2的16次方,segment数组的大小是不变的,即便后面扩容也不变。concurrenthashmap是局部扩容。每个segment的容量最小是2.
通过循环和位移计算ssize,每循环一次就左移一位,即扩大一倍。
扩容:局部扩容
    和hashmap稍微有些不同,是segment中具体的一个hashentry数组单独扩容,新建一个hashentry的数组,是原来容量的两倍,然后进行元素转移,再把新数组设置给当前segment。
    
    rehash(HashEntry<K,V> node)步骤 
        1)先把原来的hashentry数组扩容两倍
        2)遍历segment数组                             (1)第一次循环,先算出最后一个元素再扩容后的hashentry数组中的下标,并移动。第一次循环完毕后,segment每个链表的最后一个元素都移动到了新坐标的链表中。
            (2)第二次循环,把每个链表中除去最后一个元素都再一一进行移动。
        3)把node用头插法插入到扩容后的segmeng的hashentry数组中。    
    
初始化时,新建一个segment的数组,并在下标为0的地方设置一个空的segment用来存储,每个segment的容量等信息,不用后面重新计算。
UNSAVE类的加载器是bootstrap加载器。如果要在应用类里使用Unsafe可以使用反射的方式获取实例。直接new Unsafe使用时会报错。unsafe修改或获取值要根据对象以及属性变量的偏移量来获取以及修改。
put方法:
    首先根据key的hashcode和sigement数组长度-1 得出添加在哪个segment的数组元素下(下标j = (hash >>>  segmentShfit ) & segmentMask)。再根据key的hashcode和segment元素中hashentry的数组长度-1得出在hashentry数组中的下标(根据低位来算)。然后调用segment的put方法。segment的put方法在加锁。trylock()如果没加上,遍历循环期间会去做写别的事情(new  hashentry元素),创建好后,就会重试,重试一定次数后如果还是没有加锁成功,就调用lock,阻塞到获取锁,这样就停止消耗cpu资源了。如果加锁循环的过程中,hashentry数组的元素个数发生了变化(没各一次重试获取锁后都检查一次),则再重新遍历。
    
    如果segment数组下标对应的segment元素为空的时候
        首先构建一个hashentry数组,然后通过乐观锁(设置不成功就一直循环)使用unsafe的cas来设置segment下面位置的值为新建的hashentry数组。
    如果segment数组下标对应的segment元素不为空的时候,
        首先遍历hashentry数组,看看有没key相同的,如果没有,遍历到最后一个元素的时候,就new一个hashentry,然后通过头插法,插到数组中去。
    
get 方法
    根据hash算出再segment数组的下标,再找到hashentry的链表,遍历该链表,找key相同的或key和hash 都相同的,返回value值。没找到返回null。
remove方法
    long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
    根据key计算出hash,根据hash找出segment(UNSAFE.getObjectVolatile(segments,u),u是下标),然后根据key和hash调用segment的删除方法。
    segment的删除方法:
        1)尝试加锁
            如果加锁成功则去操作segment对象,如果不成功,重试加锁,重试加锁的间隙遍历链表。遍历完后再重试加锁,重试一定次数后调用lock阻塞,直到获取锁。
        2)加锁成功后执行删除逻辑
            如果删除的是第一个元素,则直接把链表的第一个元素设置为当前元素的下一个元素
            如果删除的不是第一个元素,则把要删除的元素前面元素的后一个元素设置为要删除元素的后一个元素
size方法:
    如果个数大于Integer的最大数,则返回Integer的最大数,否则返回真是数目。
    先不加锁尝试计算数目,如果计算两次都一样,说明计算的过程中元素数量没有发生改变,得出的是真实的数目。如果元素数量有改变,则给每个segment加锁,计算完成后再释放锁
sshift = 最高位前面的1前面有多少个0.
segmentmask = ssize -1 
ssize : segment数组的长度
&运算可以求余数:hash可以和(数组长度-1)的结果就是hash除(数组长度-1)的余数,即数组下标。真是太妙了


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值