线程安全集合——ConcurrentHashMap

前言

在多线程开发中,HashMap虽然性能很好,但它不是线程安全的,在并发环境下可能会出现数据不一致甚至死循环的问题。虽然我们可以使用Hashtable或Collections.synchronizedMap()来获得线程安全的Map,但它们的性能在高并发场景下并不理想。这时候,ConcurrentHashMap就派上用场了。

一、ConcurrentHashMap是什么?

ConcurrentHashMap 是Java提供的一个线程安全的哈希表实现,它是Map接口的实现类。与Hashtable不同,ConcurrentHashMap采用了更加精细的锁机制,在保证线程安全的同时,大大提高了并发性能。

主要特点:

  • 线程安全,支持高并发读写
  • 性能优于Hashtable和Collections.synchronizedMap()
  • 不允许null键和null值
  • 迭代器具有弱一致性

二、为什么需要ConcurrentHashMap?

2.1 HashMap的问题

// HashMap在多线程环境下的问题演示
Map<String, Integer> map = new HashMap<>();

// 多个线程同时操作可能导致数据不一致
new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        map.put("key" + i, i);
    }
}).start();

new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        map.put("key" + i, i);
    }
}).start();

2.2 Hashtable的局限性

// Hashtable虽然线程安全,但性能较差
Hashtable<String, Integer> hashtable = new Hashtable<>();
// 每个方法都使用synchronized,并发性能不佳

2.3 ConcurrentHashMap的优势

// ConcurrentHashMap既保证线程安全,又有更好的性能
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 采用分段锁机制(JDK 1.8后改为CAS+synchronized)

三、如何使用ConcurrentHashMap

3.1 基本用法

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        // 创建ConcurrentHashMap
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        // 基本操作
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("orange", 30);
        
        // 线程安全的获取操作
        Integer value = map.get("apple");
        System.out.println("apple的数量: " + value);
        
        // 线程安全的删除操作
        map.remove("banana");
        
        // 遍历操作
        map.forEach((key, val) -> {
            System.out.println(key + " -> " + val);
        });
        
        System.out.println("map大小: " + map.size());
    }
}

3.2 常用方法

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// 1. 基本操作
map.put("key1", 100);
map.putIfAbsent("key2", 200);  // 如果key不存在才放入
Integer value = map.get("key1");
map.remove("key1");

// 2. 原子操作方法
map.replace("key2", 200, 300);  // 原子性替换
map.compute("key3", (k, v) -> v == null ? 1 : v + 1);  // 原子性计算

// 3. 批量操作
map.putAll(Map.of("key4", 400, "key5", 500));

3.3 多线程环境下的使用

public class ConcurrentTest {
    private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    
    public static void main(String[] args) throws InterruptedException {
        // 创建多个线程同时操作
        Thread[] threads = new Thread[10];
        
        for (int i = 0; i < 10; i++) {
            final int threadId = i;
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    String key = "thread" + threadId + "_key" + j;
                    map.put(key, j);
                }
            });
            threads[i].start();
        }
        
        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("最终map大小: " + map.size());
    }
}

四、底层实现原理

4.1 JDK 1.8之前:分段锁机制

在JDK 1.8之前,ConcurrentHashMap采用分段锁(Segment)机制:

  • 将整个哈希表分成多个段(Segment)
  • 每个段都有自己的锁
  • 不同段之间的操作可以并发进行

4.2 JDK 1.8及以后:CAS + synchronized

JDK 1.8对ConcurrentHashMap进行了重大改进:

  • 取消了分段锁机制
  • 采用CAS(Compare-And-Swap)+ synchronized的方式
  • 数据结构改为数组 + 链表 + 红黑树

4.3 核心方法实现概述

put() 方法

// 简化版的put方法逻辑
public V put(K key, V value) {
    // 1. 计算hash值
    int hash = hash(key);
    
    // 2. 根据hash值找到对应的桶
    Node<K,V>[] tab = table;
    int i = (tab.length - 1) & hash;
    
    // 3. 如果桶为空,使用CAS操作插入
    if (tab[i] == null) {
        // CAS操作,无锁插入
        casTabAt(tab, i, new Node<>(hash, key, value, null));
    } else {
        // 4. 如果桶不为空,使用synchronized锁住头节点
        synchronized (tab[i]) {
            // 在链表或红黑树中查找并插入
        }
    }
}

实现概述: 通过CAS操作处理空桶的插入,对于非空桶则使用synchronized锁住头节点,这样可以最大化并发性能。

get() 方法

// 简化版的get方法逻辑
public V get(Object key) {
    // 1. 计算hash值
    int hash = hash(key);
    
    // 2. 根据hash值找到对应的桶
    Node<K,V>[] tab = table;
    int i = (tab.length - 1) & hash;
    
    // 3. 无锁读取
    Node<K,V> e = tab[i];
    while (e != null) {
        if (e.hash == hash && key.equals(e.key)) {
            return e.val;
        }
        e = e.next;
    }
    return null;
}

实现概述: 读操作完全无锁,通过volatile保证数据的可见性,极大提高了读取性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值