ConcurrentHashMap

1.7

segment.put()

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
   
    //tryLock()调用ReentrantLock的nonfairTryAcquire()
    //如果加锁没成功,调用scanAndLockForPut()方法
    HashEntry<K,V> node = tryLock() ? null :
    	scanAndLockForPut(key, hash, value);
    V oldValue;
    try {
   
        HashEntry<K,V>[] tab = table;
        int index = (tab.length - 1) & hash;
        //得到段内HashEntry[准确下标]的头结点,即得到entry链表的头
        HashEntry<K,V> first = entryAt(tab, index);
        //遍历链表
        for (HashEntry<K,V> e = first;;) {
   
            if (e != null) {
   
                K k;
                //新value覆盖旧value
                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 {
   
                // node 到底是不是 null,这个要看获取锁的过程,不过和这里都没有关系。
                // 如果不为 null,那就直接将它设置为链表表头;如果是null,初始化并设置为链表表头。
                if (node != null)
                    node.setNext(first);
                else
                    node = new HashEntry<K,V>(hash, key, value, first);
                int c = count + 1;
                // 如果超过了该 segment 的阈值,这个 segment 需要扩容
                if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                    rehash(node);
                else
                    // 没有达到阈值,将 node 放到数组 tab 的 index 位置,
                    // 其实就是将新的节点设置成原链表的表头
                    setEntryAt(tab, index, node);
                ++modCount;
                count = c;
                oldValue = null;
                break;
            }
        }
    } finally {
   
        //解锁
        unlock();
    }
    return oldValue;
}

segment.rehash()

@SuppressWarnings("unchecked")
//已经获取了独占锁,在put一开始的trylock()
private void rehash(HashEntry<K,V> node) {
   
    HashEntry<K,V>[] oldTable = table;
    int oldCapacity = oldTable.length;
    int newCapacity = oldCapacity << 1;
    threshold = (int)(newCapacity * loadFactor);
    //创建新的entry数组
    HashEntry<K,V>[] newTable =
        (HashEntry<K,V>[]) new HashEntry[newCapacity];
    int sizeMask = newCapacity - 1;
    for (int i = 0; i < oldCapacity ; i++) {
   
        //链表头
        HashEntry<K,V> e = oldTable[i];
        if (e != null) {
   
            HashEntry<K,V> next = e.next;
            // 计算应该放置在新数组中的位置,
            // 假设原数组长度为 16,e 在 oldTable[3] 处,那么 idx 只可能是 3 或者是 3 + 16 = 19
            int idx = e.hash & sizeMask;
            if (next == null)
                // 该位置处只有一个元素,直接
                newTable[idx] = e;
            else {
   
                // e 是链表表头
                // idx 是当前链表的头结点 e 的新位置
                HashEntry<K,V> lastRun = e;
                int lastIdx = idx;
                //for 循环会找到一个 lastRun 节点,这个节点之后的所有元素是将要放到一起的
                for (HashEntry<K,V> last = next;
                     last != null;
                     last = last.next) {
   
                    int k = last.hash & sizeMask;
                    if (k != lastIdx) {
   
                        lastIdx = k;
                        lastRun = last;
                    }
                }
                // 将lastRun 及其之后的所有节点组成的这个链表放到 lastIdx 这个位置
                newTable[lastIdx] = lastRun;
                // 下面的操作是处理 lastRun 之前的节点,
                // 这些节点可能分配在另一个链表中,也可能分配到上面的那个链表中
                for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
   
                    V v = p.value;
                    int h = p.hash;
                    int k = h & sizeMask;
                    HashEntry<K,V> n = newTable[k];
                    newTable[k] = new HashEntry<K,V>(h, p.key, v, n);
                }
            }
        }
    }
    // 将新来的 node 放到新数组中刚刚的 两个链表之一 的 头部
    int nodeIndex = node.hash & sizeMask; // add the new node
    node.setNext(newTable[nodeIndex]);
    newTable[nodeIndex] = node;
    table = newTable;
}

segment.scanAndLockForPut() && scanAndLock()

//没获取锁时,重试期间,就创建node节点,这样后面就不用创建了,节省时间
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
   
    //得到entry数组
    HashEntry<K,V> first = entryForHash(this, hash);
    HashEntry<K,V> e = first;
    HashEntry<K,V> node = null;
    int retries = -1;
    //循环获取锁
    while (!tryLock()) {
   
        HashEntry<K,V> f; // to recheck first below
        if (retries < 0) {
   
            //如果entry数组null
            if (e == null) {
   
                if (node == null)
                    // 进到这里说明数组该位置的链表是空的,没有任何元素
                    node = new HashEntry<K,V>(hash, key, value, null);
                retries = 0;
            }
            //entry不为null,并且key相同(同段hash必定也相同)
            else if (key.equals(e.key))
                retries = 0;
            else
                //移到下一个entry节点
                e = e.next;
        }
        //重复获取锁次数不超过默认的次数2,超过就进入到阻塞队列等待锁
        else if (++retries > MAX_SCAN_RETRIES) {
   
            lock();
            break;
        }
        //retires = 0进入,即数组不为空,头插
        else if ((retries & 1) == 0 &&
                 (f = entryForHash(this, hash)) != first) {
   
            e = first = f; // re-traverse if entry changed
            retries 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值