JDK 8 ConcurrentHashMap

初始化
ConcurrentHashMap初始化设置容量时,底层会自动转换为距设置值2倍的最近的2的次幂;
【前后距离相等时选择向后转换:如3 * 2 = 6距离4和8相等,选择向后转换为8】
jdk8的版本实际上最多可以同时扩容的线程数是:hash桶的个数/16
get方法
get方法利用了volatile特性,实现了无锁读。
查找value的过程如下:
根据key定位hash桶,通过tabAt的volatile读,获取hash桶的头结点。
通过头结点Node的volatile属性next,遍历Node链表
找到目标node后,读取Node的volatile属性val
可见上述3个操作都是volatile读,因此可以做到在不加锁的情况下,保证value的内存可见性
put方法

jdk8的实现中,锁的粒度是hash桶,因此对table数组元素的读写,大部分都是在没有锁的保护下进行的

ConcurrentHashMap中的锁是hash桶的头结点,那么当多个put线程访问头结点为空的hash桶时,在没有互斥锁保护的情况下,多个put线程都会尝试将元素插入头结点,此时如何确保并发安全呢?
在这里插入图片描述
要解决这个问题,我们要先了解什么是CAS操作:CAS,即compareAndSwap,原子的执行比较并交换的操作:当前内存值如果和预期值相等,则将其更新为目标值,否则不更新。

然后,我们看下ConcurrentHashMap的put方法是如何通过CAS确保线程安全的:

假设此时有2个put线程,都发现此时桶为空,线程一执行casTabAt(tab,i,null,node1),此时tab[i]等于预期值null,因此会插入node1。随后线程二执行casTabAt(tba,i,null,node2),此时tab[i]不等于预期值null,插入失败。然后线程二会回到for循环开始处,重新获取tab[i]作为预期值,重复上述逻辑。

    final V putVal(K key, V value, boolean onlyIfAbsent) {
        ...
        for (Node<K,V>[] tab = table;;) {
            ...
            //key定位到的hash桶为空
            if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                //cas设置tab[i]的头结点。
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;   //设置成功,跳出for循环
                //设置失败,说明tab[i]已经被另一个线程修改了。回到for循环开始处,重新判断hash桶是否为空。如何往复,直到设置成功,或者hash桶不空。
            }else{
               synchronized (f) {
                   //
               }
                
            }
        }
        ...
    }

以上通过for循环+CAS操作,实现并发安全的方式就是无锁算法(lock free)的经典实现

    //JDK7版本的 AtomicInteger 类的原子自增操作
    public final int getAndIncrement() {
        for (;;) {
            //获取value
            int current = get();
            int next = current + 1;
            //value值没有变,说明其他线程没有自增过,将value设置为next
            if (compareAndSet(current, next))
                return current;
            //否则说明value值已经改变,回到循环开始处,重新获取value。
        }
    }

由于锁的粒度是hash桶,多个put线程只有在请求同一个hash桶时,才会被阻塞。请求不同hash桶的put线程,可以并发执行。

put线程,请求的hash桶为空时,采用for循环+CAS的方式无锁插入。

在这里插入图片描述
remove方法
和put方法一样,多个remove线程请求不同的hash桶时,可以并发执行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值