对ConcurrentHashMap1.7的一些理解

ConcurrentHashMap 1.7  

    数据结构:每一个实际存放元素的HashEntry数组由不同的Segment锁保护,HashEntry是一个链表。

1.put 

    put时先定位到Segment上,

s = (Segment<K,V>)UNSAFE.getObject(segments, (j << SSHIFT) + SBASE) 

     这里没有用volatile读是因为如果Segment已经初始化,后续不会对它的reference进行更改,如果没有初始化,会在ensureSegment()中进行volatile读校验。
     然后对该Segment尝试进行加锁,失败后会自旋tryLock一定次数,如果还没获取到锁,调用lock阻塞,等待获取锁。
     获取锁之后,接着put过程就和HashMap差不多了,定位到HashEntry数组的下标,然后使用链地址法放入元素。最后在finally中释放锁。
     因为每次只锁住一个Segment,所以多个Segment的put可以并发执行。最多支持Segment数组长度的并发数。

2.get     

     在说get前首先提一点:Segment的table属性是volatile的,HashEntry的value和next属性是volatile的,对于一个volatile变量的写操作先行发生于之后对该volatile变量的读操作。
     get操作不会获取锁。这可能会导致前一个线程修改了map对另一个线程不会立即可见。
     首先定位到Segment,这里用volatile读Segment,保证并发read和write时Segment已经初始化但对其他线程不可见的问题。s.table是volatile的,另一个线程初始化了之后,此线程可见。

if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
	(tab = s.table) != null) {
	// 对volatile修饰的对象或数组而言,其含义是对象或数组的引用具有可见性,但是数组或对象内部的成员改变不具备可见性。 
	// 对于加锁的put和没有加锁的get之间并不存在happens-before 所以新增Entry时要volatile进行put 此处volatile进行get
	// volatile的value和next保证了别的线程修改Entry后对此线程立即可见
	for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
			 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
		 e != null; e = e.next) {
		K k;
		if ((k = e.key) == key || (e.hash == h && key.equals(k)))
			return e.value;
	}
}

3.size

     size分两步,
     第一个步遍历所有的segment,累加modCount和count,如果前后两次遍历累加的modCount得到一样的值,就返回累加的count。
     如果重试次数==RETRIES_BEFORE_LOCK(第三次循环满足该条件,也就是比较两次都不一致后),会锁住所有的segment,并进行上一步的计算。此循环最多执行5次,最少执行2次。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值