ConcurrentHashMap

1. 线程安全性

  • ConcurrentHashMap 支持多线程并发访问,不需要额外的同步措施,因此可以在多线程环境中安全使用。

2. 分段锁机制

  • ConcurrentHashMap 使用分段锁(Segment)机制,将整个哈希表分成多个段(默认为16个)。
  • 每个段上有一个独立的锁,这意味着不同的线程可以同时访问不同的段,从而提高了并发性能。
  • 在写操作时,只会对所操作的段加锁,而不影响其他段的访问。

3. 高并发性能

  • 由于分段锁机制,ConcurrentHashMap 具有良好的并发性能。
  • 多个线程可以同时读取不同的段,只有在写操作时才会涉及到锁的竞争。

4. 支持高效的读操作

  • 读操作在没有竞争的情况下几乎没有性能损耗,因为它们可以并行执行。

5. 适用于大规模数据

  • ConcurrentHashMap 适用于处理大规模数据集,因为它的性能不会随数据规模的增加而线性下降。

6. 支持原子操作

  • ConcurrentHashMap 提供了一些原子操作,如 putIfAbsentremovereplace 等,这些操作可以在不需要额外的同步措施的情况下执行。

7. 高度可扩展

  • 由于分段锁的机制,ConcurrentHashMap 具有良好的可扩展性,可以适应不同并发级别的需求。

8. 不允许空键或空值

  • ConcurrentHashMap 不允许键或值为空(null),如果尝试插入空键或空值,会抛出 NullPointerException。

9. 底层数据结构

  • ConcurrentHashMap 的底层数据结构是一个 Segment 数组,每个 Segment 实例又包含若干个桶,每个桶中都包含一条由若干个 HashEntry 对象链接起来的链表。
  • 在 Java 8 及以上版本中,当链表长度超过阈值(TREEIFY_THRESHOLD,默认为 8)并且数组长度大于或等于 64 时,链表会转换为红黑树以提高查询效率。

10. 使用场景

  • ConcurrentHashMap 通常用于需要频繁读写的并发环境下,比如高并发的 Web 应用程序中的缓存系统。它不仅提供了线程安全的操作,还提供了更好的性能,因为它通过分段锁实现了并发的读写操作,不会对整个哈希表进行加锁。

    /**
     * 向映射中插入键值对,如果键已存在则根据onlyIfAbsent决定是否替换原有值。
     * 此方法是put和putIfAbsent操作的核心实现。
     *
     * @param key 映射中的键,不允许为null。
     * @param value 与键关联的值,不允许为null。
     * @param onlyIfAbsent 如果为true且键已存在,则不更新已有值;否则,将值替换为新值。
     * @return 如果键已存在且onlyIfAbsent为true,返回旧值;否则返回null。
     */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        // 检查键和值是否为空,若任一为空则抛出异常
        if (key == null || value == null) throw new NullPointerException();

        // 计算键的哈希值并应用扩散函数
        int hash = spread(key.hashCode());

        // 初始化桶内节点计数器
        int binCount = 0;

        // 主循环处理table(散列表)
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; // 当前桶的头结点
            int n, // table的长度
                i, // 计算出的索引位置
                fh; // 头结点的哈希值

            // 初始化或扩容table
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();

            // 处理空桶,使用CAS无锁添加新节点
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                    break;
            }

            // 处理正在进行的transfer操作
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            // 处理非空桶
            else {
                V oldVal = null;
                // 对头结点同步,确保线程安全
                synchronized (f) {
                    // 再次检查table[i]是否未被其他线程修改
                    if (tabAt(tab, i) == f) {
                        // 链表处理逻辑
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                // 查找匹配的键,更新或插入值
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key, value, null);
                                    break;
                                }
                            }
                        }
                        // 红黑树处理逻辑
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }

                // 检查是否需要转换为红黑树或返回旧值
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }

        // 更新size相关统计信息
        addCount(1L, binCount);
        return null;
    }

综上所述,ConcurrentHashMap 是在多线程环境中处理哈希表数据时非常有用的数据结构,它提供了高性能的并发访问能力,并通过分段锁机制来减少竞争,使得多线程访问哈希表更加高效和安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值