对ConcurrentHashMap的remove操作解析

本文详细解析了ConcurrentHashMap中remove方法的工作原理,包括如何通过hash值定位元素,链表中元素的查找与删除过程,以及Segment内部remove方法的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  ConcurrentHashMap的remove源码:

/**
     * Removes the key (and its corresponding value) from this map.
     * This method does nothing if the key is not in the map.
     *
     * @param  key the key that needs to be removed
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
     * @throws NullPointerException if the specified key is null
     */
    public V remove(Object key) {
	int hash = hash(key.hashCode());
        return segmentFor(hash).remove(key, hash, null);
    }

上面的删除方法:获取该key的hash值,找到所属的桶(如果不知道桶可以去看下ConcurrentHashMap的实现原理),并在桶中执行删除操作

Segment<K,V>重写了这个remove方法,源码:

/**
         * Remove; match on key only if value null, else match both.
         */
        V remove(Object key, int hash, Object value) {
            lock();
            try {
                int c = count - 1;
                HashEntry<K,V>[] tab = table;
                int index = hash & (tab.length - 1);
                HashEntry<K,V> first = tab[index];
                HashEntry<K,V> e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                V oldValue = null;
                if (e != null) {
                    V v = e.value;
                    if (value == null || value.equals(v)) {
                        oldValue = v;
                        // All entries following removed node can stay
                        // in list, but all preceding ones need to be
                        // cloned.
                        ++modCount;
                        HashEntry<K,V> newFirst = e.next;
                        for (HashEntry<K,V> p = first; p != e; p = p.next)
                            newFirst = new HashEntry<K,V>(p.key, p.hash,
                                                          newFirst, p.value);
                        tab[index] = newFirst;
                        count = c; // write-volatile
                    }
                }
                return oldValue;
            } finally {
                unlock();
            }
        }

上面的解析:首先为该桶加锁,通过hash值找到该链在桶中的位置(如果不知道链可以去看下hashMap的实现原理),while循环操作是为了找到需要删除的元素,类似于不断循环,当不是当前hashEntry的时候就next,直接找到.然后判断value==null,,可以看下第一段源码中,传入的参数为null,所以进入if(value==null||value.euqals(v))),在该方法中的HashEntry<K,V> newFirst=e.next.,从新建立该桶下的序列号下的链,当前entry的next为该需要删除元素的下一个元素,并循环原来的真个链,所有不是这个删除元素的都在循环之中,在第一次循环中,假设有1,2,3,4这4个元素,需要删除的为3号元素,第一次循环之后,newFirst中原来的第一个元素为1,next为4,所以第一次循环之后为1,4,第二次循环之后元素到了2,所有2的下一个元素定位1,所以顺序为2,1,4,到了第三个元素,为3时,由于p==e,则跳过,由于p.next没有了,则跳出循环,固重组之后的顺序为2,1,4.关键的代码为:

                        HashEntry<K,V> newFirst = e.next;
                        for (HashEntry<K,V> p = first; p != e; p = p.next)
                            newFirst = new HashEntry<K,V>(p.key, p.hash,
                                                          newFirst, p.value);

 

### ConcurrentHashMap 的源码解析与并发容器实现原理 #### 数据结构设计 `ConcurrentHashMap` 是 Java 中一种高效的线程安全哈希表实现。它通过分段锁(Segment Locking)机制来减少锁的竞争,从而提高性能[^1]。在 JDK 8 及之后版本中,`ConcurrentHashMap` 进一步优化了其实现方式,采用了 CAS(Compare And Swap)、锁分离以及红黑树等技术。 #### 初始化过程 当创建 `ConcurrentHashMap` 实例时,默认会初始化一个空的数组,并设置初始容量和加载因子。如果指定了初始容量,则会计算出最接近该值的 2 的幂次方作为实际容量[^2]。以下是部分初始化代码: ```java public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0) || InitialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); if (initialCapacity < concurrencyLevel) initialCapacity = concurrencyLevel; long size = (long)(initialCapacity / loadFactor); int cap = ((int)size >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)size + 1); this.segments = Segment.newArray(concurrencyLevel); } ``` #### 锁定策略 JDK 7 版本中的 `ConcurrentHashMap` 使用的是 **分段锁** 技术,即将整个数据结构划分为多个独立的部分(称为 Segments),每个 Segment 都是一个小型的 HashTable。这样可以显著降低锁冲突的概率[^3]。 然而,在 JDK 8 和更高版本中,这种基于 Segment 的锁定被移除,转而采用更细粒度的节点级加锁方法。对于链表长度超过一定阈值的情况,还会将链表转换成红黑树以提升查询效率[^4]。 #### 插入操作分析 执行 put 操作时,首先定位到对应的桶位置;如果是首次插入或者目标桶为空,则直接利用无锁的方式完成写入尝试。否则进入竞争状态并获取相应级别的独占权限后再继续后续逻辑处理[^5]。 #### 删除操作详解 删除键值对的过程同样遵循类似的路径规划原则——先找到待移除项所在的具体索引地址,再判断当前存储形式(单向链接列表还是平衡二叉搜寻树)。最后依据实际情况分别调用不同的清理函数达成目的[^6]。 ```java final V removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { ... } ``` 以上展示了核心功能模块之一removeNode() 方法的大致框架结构图解说明文档片段。 #### 性能考量因素 由于引入了多种同步控制手段,因此即使是在高负载环境下也能保持较好的吞吐量表现。但是需要注意的是,过多的读取请求可能会因为缓存一致性协议而导致 CPU 缓存失效现象发生,进而影响整体运行速度[^7]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值