ConcurrentHashMap的Get()和Put()方法

jdk 1.7

get()方法
  • 首先获取到concurrentHashmap中segments数组的下标,找到对应的segment,然后找到对应的hashentrys数组,通过hash值计算获取下标,遍历hashentrys中指定下标的链表
  • 从中判断是否存在hashentry满足输入的key的条件,如果有,则返回对应的value,如果没有,返回为空.
public V get(Object key) {
        Segment<K,V> s; // manually integrate access methods to reduce overhead
        HashEntry<K,V>[] tab;
        int h = hash(key);
        // 获取segments 数组的的下标
        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
        // 获取segments 中指定下标的 segment  并获取到segment中的hashentry数组
        if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
            (tab = s.table) != null) {
            // 遍历 hashEntry数组中指定下标的链表
            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)))
                    // 如果存在要查询的key,返回旧值
                    return e.value;
            }
        }
        // 否则,返回为空
        return null;
    }
put()方法
  • 首先判断输入的value的值是否为空,为空,抛出空指针异常
  • 如果不为空,计算key的hash值,并计算出segments数组的下标,通过unsafe.getobject()方法尝试获取对应下标的segment,如果为空,则直接从segments数组中获取
  • 获取到segment后,调用它的put方法进行存储
  • segment的put方法中,首先尝试获取锁对象,如果获取成功,获取hashentrys数组,通过计算hash值,获取hashentry数组的下标,找到对应的链表,
  • 遍历链表,首先判断遍历获取到的hashentry对象是是否为空,如果为空,说明没有与key符合的旧key,判断当前数组中的元素是否超过阈值,如果超过,进行扩容,如果没有超过,则新添加一个,并设置为链表的头部,并放在数组中.
  • 如果链表不为空,则判断是否满足key和hash的条件,如果满足,则设置获取旧值,判断是否可以对旧值进行复制,如果可以,新值覆盖旧值,返回旧值,然后释放锁,返回旧值

segments数组不变,segment中的table数组会进行扩容,扩容后(oldseze * 2), 初始大小是16, key和value都不能为空

    @SuppressWarnings("unchecked")
    public V put(K key, V value) {
        Segment<K,V> s;
        // 判断 valu的值是否为空,如果为空,抛出空指针异常
        if (value == null)
            throw new NullPointerException();
        // 计算key的hash值
        int hash = hash(key);
        // 通过hash值获取到segment的下标  segmentShift默认是4,segmentMask默认是15
        int j = (hash >>> segmentShift) & segmentMask;
        // 通过unsafe的getObject()方法获取为空时,从数组中获取对应的segment
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
            // 调用segment的put方法存储key - value
        return s.put(key, hash, value, false);
    }

segment中的put方法

    // segment中的 put方法
    final V put(K key, int hash, V value, boolean onlyIfAbsent) {
        // 对segment 尝试加锁,如果成果,node为null,如果失败node为scanAndLockForPut()的返回值
        // scanAndLockForPut 先通过自旋的方式尝试获取锁,中间如果锁被释放,直接获取锁,如果获取的次数如果超过了最大自旋次数,变为等待锁
        HashEntry<K,V> node = tryLock() ? null :
            scanAndLockForPut(key, hash, value);
        V oldValue;
        try {
            // 获取segment中的 数组信息
            HashEntry<K,V>[] tab = table;
            // 获取segment中 hashentry数组的下标
            int index = (tab.length - 1) & hash;
            // 通过数组和下标获取对应的hashentry对象(链表)
            HashEntry<K,V> first = entryAt(tab, index);
            // 遍历链表
            for (HashEntry<K,V> e = first;;) {
                // 判断获取到的链表是否为空
                if (e != null) {
                    K k;
                    // 不为空,判断hash值和key值是否相同
                    if ((k = e.key) == key ||
                        (e.hash == hash && key.equals(k))) {
                        // 如果符合条件,获取旧值
                        oldValue = e.value;
                        // 判断是否允许修改旧值,如果同意,新值覆盖旧值
                        if (!onlyIfAbsent) {
                            e.value = value;
                            ++modCount;
                        }
                        // 跳出循环
                        break;
                    }
                    e = e.next;
                }
                else {
                    // 如果 获取到的链表为空,说明对应的table下标数组内还没有添加元素, 把新添加的元素放在链表的头部
                    if (node != null)
                        node.setNext(first);
                    else
                        node = new HashEntry<K,V>(hash, key, value, first);
                    int c = count + 1;
                    // 判断当前元素的数量是否超过临界值,如果超过了,进行扩容
                    if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                        rehash(node);
                    else
                        // 如果没超过,则添加到hashentry数组中
                        setEntryAt(tab, index, node);
                    ++modCount;
                    count = c;
                    // 由于是新添加的,所有返回的value为空
                    oldValue = null;
                    // 跳出循环
                    break;
                }
            }
        } finally {
            // 释放锁
            unlock();
        }
        // 返回旧值
        return oldValue;
    }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值