ConcurrentHashMap在jdk1.8中做了什么优化

ConcurrentHashMap在JDK1.7中使用分段锁技术,每个Segment是一个可重入锁,减少并发冲突。JDK1.8中取消Segments,直接用table数组并用其元素加锁,同时引入红黑树优化长链表的查询性能,当链表长度超过8时转为红黑树。这些改进旨在提高并发性和性能。
摘要由CSDN通过智能技术生成

简单回顾ConcurrentHashMap在jdk1.7中的设计

与Hashtable不同的是,ConcurrentHashMap使用的是分段锁的技术,将ConcurrentHashMap容器的数据分段存储,每一段数据就分配一个Segment,当线程占用一个Segment时,其他线程可以访问其他段的数据(每个segment都是一个锁)。与HashTable相比,这么设计的目的是对于put、remove等操作,可以减少并发冲突,对不属于同一个片段的节点可以并发操作,大大提高了性能。
(1)Segment:可重入锁(在Java环境下ReentrantLock和synchronized都是可重入锁),继承自ReentrantLock,也称之为桶(本质上Segment类就是一个小型的hashMap),每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获取到它对应的Segment锁。
(2)HashEntry:主要存储键值对,这里也可以叫节点
HashEntry源码:

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

其中,volatile关键字保证了多线程读取到的一定是最新值,ConcurrentHashMap包含一个Segment数据,每个Segment包含一个HashEntry数组。

ConcurrentHashMap在jdk1.8中做了两方面的改进

改进一:取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。(transient修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略,因为transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化)。

改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数据中。如果hash之后的散列很均匀,那么table数组中的每个队列的长度为0或者1。但是实际情况并非总是如此理想,虽然ConcurrentHashMao默认的加载因子为0.75。(加载因子是衡量一个哈希表何时需要扩容,也就是增加存储空间的阈值,加载因子的值是哈希表中已经存储的键值对与哈希表长度的比值,当这个比值超过加载因子时,哈希表就会进行扩容)。但是在数据量过大或者运气不佳的情况下还是会存在一些队列长度过长的情况,如果还是采用单向链表的方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过默认值8的列表,jdk1.8采用了红黑树的结构,那么此时查询的时间复杂度可以降低到O(logN),以此改进性能。

为什么阈值为8

(1)红黑树平均查找时间复杂度为log(N),log(8) = 3,链表平均查找长度为N/2, 8/2 = 4,转换后性能更高。
(2)立项情况下,hash桶中的节点的频率遵循泊松分布,桶长度超过8的概率非常小,通常情况下不会发生结构改变。

为什么不直接使用红黑树

(1)因为二叉树(红黑树)虽然查询效率高,但是空间开销很大,单个TreeNode需要占用的空间大约是普通Node的两倍,而在数据量很小时,红黑树与链表的查询效率不会差太多。
(2)因为桶长度超过8的概率很小,所以大概率情况下链表是更好的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ConcurrentHashMap 是 JDK1.5 引入的线程安全的哈希表,它在 JDK1.8 的底层实现主要包括以下几个方面: 1. Segment 分段锁 ConcurrentHashMap 内部使用了 Segment 分段锁来保证线程安全。Segment 是 ConcurrentHashMap 的一个内部类,每个 Segment 代表一个哈希桶数组的一段。在对 ConcurrentHashMap 进行操作时,只需要获取对应的 Segment 的锁,而不是整个 ConcurrentHashMap 的锁,从而提高了并发效率。 2. 数组 + 链表 + 红黑树的混合结构 ConcurrentHashMap 内部使用了数组 + 链表 + 红黑树的混合结构来存储数据。在 JDK1.8 ,当哈希桶的链表长度超过了一定阈值(默认为 8),会将链表转化为红黑树,从而提高查找效率。 3. CAS 操作 ConcurrentHashMap 内部使用了 CAS 操作(Compare and Swap)来实现线程安全。CAS 操作是一种无锁算法,它可以在多线程环境下保证数据的原子性操作。在 ConcurrentHashMap ,当多个线程同时对同一个桶进行操作时,会使用 CAS 操作来保证数据的正确性。 4. 优化的扩容机制 ConcurrentHashMapJDK1.8 对扩容机制进行了优化,使用了类似于链表的方式来实现数据的迁移,从而减少了数据的移动次数,提高了扩容的效率。 总之,ConcurrentHashMapJDK1.8 的底层实现主要依靠 Segment 分段锁、数组 + 链表 + 红黑树的混合结构、CAS 操作和优化的扩容机制来实现线程安全和高效的并发操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值