集合-ConcurrentHashMap-线程安全底层实现

本文探讨了JDK1.7中ConcurrentHashMap的分段锁设计,每个Segment守护一个HashEntry数组,确保线程安全。而JDK1.8则取消分段锁,采用CAS和synchronized优化并发性能。与HashTable相比,两者都是线程安全的,但ConcurrentHashMap在并发访问效率上更胜一筹。

1、ConcurrentHashMap分段锁

  • JDK1.7是采用分段锁的思想,对整个桶数组进行切割分段(segment),给每一段数据配置一把锁,当一个线程占用锁访问一段数据的时候,其他的数据也能被其他的线程访问到;

  • ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成的:Segment实现了ReentrantLock,是一种可重入锁,来扮演锁的角色;HashEntry用于存储键值对数据。

  • static class Segment<K,V> extends ReentrantLock implements Serializable{}
    
  • 一个ConcurrentHashMap包含一个Segment数组(是一种数组和链表结构),一个Segment包含一个HashEntry数组,每个HashEntry是 一个链表结构的元素,一个Segment守护一个HashEntry数组,要想对HashEntry数组进行修改就必须先获得Segment的锁

在这里插入图片描述

  • JDK1.8取消了Segment分段锁,采用CAS和synchronized来保证线程安全,数据结构和jdk1.8的HashMap类似,synchronized只锁定当前链表/红黑树的首节点,这样只要hash不冲突就不会产生并发,提高效率N

在这里插入图片描述

2、ConcurrentHashMap和HashTable的区别?

  • 底层数据结构:jdk1.7的ConcurrentHashMap的底层采用分段的数组和链表实现,jdk1.8之后和HashMap一样采用数组+链表/红黑树,HashTable的底层数据结构是数组+链表

  • 线程安全方面:都是线程安全的;

    • 实现线程安全的方式区别:jdk1.7时,ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(segment),每一把锁只锁容器中的一部分数据,多线程访问容器的不太段的数据的时候就不会存在锁竞争,提高并发访问率。jdk1.8就摒弃了segment分割分段的思路,直接用Node数组+链表+红黑树的数据结构来实现,并发控制用synchronized和CAS来操作。 HashTable使用synchronized来保证线程安全,效率很低
### HashMap底层原理 `HashMap` 是基于哈希表实现的键值对集合。其核心结构是数组 + 链表(在 Java 8 及之后引入了红黑树优化长链表)。每个键值对被封装为一个 `Node` 对象,其中包含键、值和哈希值。 1. **哈希计算**:当调用 `put(K key, V value)` 方法时,首先会通过 `hashCode()` 方法计算键的哈希值,并将其映射到数组的一个索引位置。 2. **冲突解决**:如果多个键映射到同一个索引位置,则使用链地址法解决冲突。即在该索引位置上维护一个链表或红黑树来存储这些键值对。 3. **扩容机制**:当元素数量超过容量乘以负载因子(默认为 0.75)时,`HashMap` 会进行扩容操作,将原来的数组大小翻倍,并重新分配所有键值对的位置[^3]。 由于 `HashMap` 没有内置的同步机制,在多线程环境下进行写操作可能会导致数据不一致问题,例如死循环或者丢失更新等[^1]。 --- ### ConcurrentHashMap 线程安全实现 `ConcurrentHashMap` 是专门为并发环境设计的线程安全 `Map` 实现。它采用了分段锁技术来提高并发性能: 1. **Segment 分段锁**:`ConcurrentHashMap` 内部由多个 `Segment` 组成,每个 `Segment` 实际上是一个小型的 `HashMap`,并且继承了 `ReentrantLock` 类,可以独立加锁。这意味着不同的线程可以在不同的 `Segment` 上同时执行操作而不会相互阻塞[^4]。 2. **HashEntry 数组**:每个 `Segment` 包含一个 `HashEntry` 数组,用于实际存储键值对。与 `HashMap` 类似,`HashEntry` 也支持链表和红黑树结构来处理哈希冲突。 3. **细粒度锁定**:当需要修改某个 `Segment` 中的数据时,只需要对该 `Segment` 加锁即可,而不是对整个 `ConcurrentHashMap` 进行全局加锁。这种细粒度的锁定策略大大提高了高并发场景下的吞吐量[^2]。 以下是一个简单的示例代码展示如何创建并使用 `ConcurrentHashMap`: ```java import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { // 创建一个ConcurrentHashMap实例 ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); // 添加键值对 map.put("one", 1); map.put("two", 2); // 获取值 System.out.println(map.get("one")); // 输出: 1 // 并发更新 map.computeIfPresent("two", (key, val) -> val + 1); System.out.println(map.get("two")); // 输出: 3 } } ``` 上述代码展示了基本的操作如添加、获取以及并发更新。值得注意的是,`ConcurrentHashMap` 提供了一系列原子性方法如 `computeIfPresent`, `merge` 等,这些方法能够在保证线程安全的同时提供更高的效率。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值