专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
芋道源码  ·  SpringBoot如何做到无感刷新token? ·  5 小时前  
Java编程精选  ·  Controller层代码这么写,简洁又优雅! ·  2 天前  
Java编程精选  ·  因“过于痴迷”AI,50+岁程序员被公司开除 ... ·  昨天  
芋道源码  ·  5.6K ... ·  2 天前  
芋道源码  ·  手把手教你实现一个Java Agent ·  2 天前  
51好读  ›  专栏  ›  ImportNew

谈谈 ConcurrentHashMap1.7 和 1.8 的不同实现

ImportNew  · 公众号  · Java  · 2017-02-26 20:17

正文

请到「今天看啥」查看全文



场景:线程A和线程B同时执行相同Segment对象的put方法


  1. 线程A执行tryLock()方法成功获取锁,则把HashEntry对象插入到相应的位置;


  2. 线程B获取锁失败,则执行scanAndLockForPut()方法,在scanAndLockForPut方法中,会通过重复执行tryLock()方法尝试获取锁,在多处理器环境下,重复次数为64,单处理器重复次数为1,当执行tryLock()方法的次数超过上限时,则执行lock()方法挂起线程B;


  3. 当线程A执行完插入操作时,会通过unlock()方法释放锁,接着唤醒线程B继续执行;


size实现


因为ConcurrentHashMap是可以并发插入数据的,所以在准确计算元素时存在一定的难度,一般的思路是统计每个Segment对象中的元素个数,然后进行累加,但是这种方式计算出来的结果并不一样的准确的,因为在计算后面几个Segment的元素个数时,已经计算过的Segment同时可能有数据的插入或则删除,在1.7的实现中,采用了如下方式:


try {

for (;;) {

if (retries++ == RETRIES_BEFORE_LOCK) {

for (int j = 0; j

ensureSegment(j).lock(); // force creation

}

sum = 0L;

size = 0;

overflow = false;

for (int j = 0; j

Segment seg = segmentAt(segments, j);

if (seg != null) {

sum += seg.modCount;

int c = seg.count;

if (c

overflow = true;

}

}

if (sum == last)

break;

last = sum;

}

} finally {

if (retries > RETRIES_BEFORE_LOCK) {

for (int j = 0; j

segmentAt(segments, j).unlock();

}

}


先采用不加锁的方式,连续计算元素的个数,最多计算3次:


  1. 如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;


  2. 如果前后两次计算结果都不同,则给每个Segment进行加锁,再计算一次元素的个数;


1.8实现


数据结构


1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,结构如下:








请到「今天看啥」查看全文