【数据类型】ConcurrentHashMap分段锁实现高并发;与HashMap的区别

一、ConcurrentHashMap介绍

ConcurrentHashMap 是 Java 中的线程安全的哈希表实现,它提供了一种高效的并发访问机制,适用于多线程环境。下面是 ConcurrentHashMap 的一些特性和用法:

  • 线程安全性: ConcurrentHashMap 提供了线程安全的操作。 多个线程可以同时读取和修改映射,而不会导致数据不一致或抛出并发修改异常 \color{green} {多个线程可以同时读取和修改映射,而不会导致数据不一致或抛出并发修改异常} 多个线程可以同时读取和修改映射,而不会导致数据不一致或抛出并发修改异常
  • 分段锁: ConcurrentHashMap 内部采用分段锁的机制, 将整个映射分成一系列独立的段,每个段拥有自己的锁。 \color{green}{将整个映射分成一系列独立的段,每个段拥有自己的锁。} 将整个映射分成一系列独立的段,每个段拥有自己的锁。这使得多个线程可以同时访问不同的段,提高了并发性。
  • 并发度: 并发度是指 ConcurrentHashMap 中独立锁的个数。默认的并发度为 16,可以在构造方法中指定。更高的并发度可以提高并发性,但也会增加内存开销。
  • 无锁读取: 读取操作不需要加锁,因此可以实现高效的并发读取。
  • 原子性操作: ConcurrentHashMap 提供了一些原子性的操作方法,如 putIfAbsentreplaceremove 等。
  • 迭代器弱一致性: ConcurrentHashMap 的迭代器是弱一致的,意味着在迭代过程中,其他线程可能会对映射进行修改,但迭代器不会抛出 ConcurrentModificationException 异常。

二、简单的示例:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();

        concurrentMap.put("one", 1);
        concurrentMap.put("two", 2);
        concurrentMap.put("three", 3);

        // 并发安全的修改
        concurrentMap.compute("one", (key, value) -> value + 10);

        // 获取值
        System.out.println(concurrentMap.get("one")); // 输出 11

        // 遍历
        concurrentMap.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}

在实际应用中,ConcurrentHashMap 通常用于需要高并发读写的场景,例如缓存、计数器等 \color{green}{通常用于需要高并发读写的场景,例如缓存、计数器等} 通常用于需要高并发读写的场景,例如缓存、计数器等

三、与HashMap的区别

ConcurrentHashMap 和 HashMap 是 Java 集合框架中的两个不同的实现,它们之间有一些关键的区别:

1、并发性:

  • HashMap 是非线程安全的。在多线程环境下,如果多个线程同时访问和修改 HashMap,可能导致不确定的结果,甚至可能导致数据损坏。
  • ConcurrentHashMap 是线程安全的,通过分段锁的机制,它允许多个线程同时读取和写入映射,提高了并发性能。

2、锁机制:

  • HashMap 没有提供内置的锁机制,需要外部进行同步控制。
  • ConcurrentHashMap 使用分段锁(Segment ocks)的机制,将整个映射分成多个段,每个段都有一个独立的锁。这样在进行写操作时只需锁住对应的段,而不是整个映射。

3、读操作的性能:

  • HashMap 在进行读操作时不需要获取锁,但在进行写操作时需要同步控制,可能导致读操作阻塞。
  • ConcurrentHashMap 使用分段锁的方式,允许多个线程同时进行读操作,不会阻塞。

4、迭代器的弱一致性:

  • HashMap 在迭代器遍历时,如果在迭代过程中有其他线程对集合进行了修改,可能会抛出
    ConcurrentModificationException 异常。
  • ConcurrentHashMap 提供了弱一致性的迭代器,允许在迭代过程中对集合进行修改。

5、初始容量和负载因子:

总的来说,ConcurrentHashMap 是为了在多线程环境中提供高效的并发访问而设计的,而 HashMap 则适用于单线程环境或者在多线程环境中使用外部同步机制的情况。选择使用哪个取决于具体的应用场景和性能需求。

四、进阶-源码讲解

下面将展示其中一部分关键代码,并对其进行简要解释。

Java 8 版本的 ConcurrentHashMap 中的关键部分:

public class ConcurrentHashMap<K, V> {
    // ...

    // Segment 数组,每个 Segment 代表一个段
    final Segment<K, V>[] segments;

    // ...

    // Segment 类,实现了 ReentrantLock 接口
    static final class Segment<K, V> extends ReentrantLock {
        // HashEntry 数组,用于存储键值对
        transient volatile HashEntry<K, V>[] table;

        // ...

        // put 操作
        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
            HashEntry<K, V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
            try {
                HashEntry<K, V>[] tab = table;
                int index = (tab.length - 1) & hash;
                HashEntry<K, V> first = entryAt(tab, index);
                for (HashEntry<K, V> e = first; ; e = e.next) {
                    if (e == null) {
                        if (node == null)
                            node = new HashEntry<K, V>(hash, key, value, first);
                        else
                            node.next = first;
                        setEntryAt(tab, index, node);
                        break;
                    }
                    K k;
                    if ((k = e.key) == key || (e.hash == hash && key.equals(k))) {
                        V oldValue = e.value;
                        if (!onlyIfAbsent || oldValue == null)
                            e.value = value;
                        return oldValue;
                    }
                }
            } finally {
                unlock();
            }
        }

        // 其他方法...

        static final class HashEntry<K, V> {
            final int hash;
            final K key;
            volatile V value;
            final HashEntry<K, V> next;

            HashEntry(int hash, K key, V value, HashEntry<K, V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
        }
    }

    // ...
}

解释:

ConcurrentHashMap 内部维护了一个 Segment 数组,每个 Segment 对应一个段,这些段之间相互独立,各自管理自己的哈希桶。

Segment 类继承了 ReentrantLock,表示一个可重入锁,每个 Segment 对应一个哈希桶。在对某个段进行操作时,通过 ReentrantLock 来进行加锁,实现对该段的互斥访问。

每个 Segment 中有一个 HashEntry 数组,用于存储键值对HashEntry 是键值对的节点,采用链表法处理哈希冲突

put 方法是 ConcurrentHashMap 中进行 put 操作的关键方法。在进行 put 操作时,先通过哈希值找到对应的段,然后在该段的哈希桶中查找键值对。如果找到了相同的键,则更新值;如果没有找到相同的键,则将新的键值对插入到链表的头部

这只是 ConcurrentHashMap 中的一小部分关键代码,该类的实现非常复杂,包含了诸多细节,如哈希桶的扩容、红黑树的引入等。希望这部分代码能够帮助你理解 ConcurrentHashMap 的基本设计思路。

持续更新中,动动小手,点点关注,后续更精彩~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值