MST

星途 面试题库

面试题:Java线程安全集合类ConcurrentHashMap的并发度及优化机制

Java的ConcurrentHashMap中并发度是如何定义和实现的?它在高并发场景下有哪些优化机制来提升性能?
47.8万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

并发度的定义与实现

  1. 定义:在ConcurrentHashMap中,并发度(concurrencyLevel)指的是该哈希表能够同时支持的并发更新操作的大致数量。它实际上是ConcurrentHashMap中所划分的段(Segment)的数量。每个段都是一个独立的哈希表结构,这样不同线程可以同时操作不同的段,从而实现并发访问。
  2. 实现:在JDK 1.7及以前,ConcurrentHashMap由多个Segment组成,每个Segment继承自ReentrantLock,包含一个哈希表数组。concurrencyLevel在构造函数中初始化,默认值为16,即创建16个Segment。当向ConcurrentHashMap中插入或获取元素时,首先通过哈希值定位到具体的Segment,然后对该Segment加锁进行操作。在JDK 1.8中,ConcurrentHashMap摒弃了Segment的概念,采用数组 + 链表 + 红黑树的数据结构,通过CAS(Compare And Swap)操作和Synchronized关键字来保证并发安全。虽然不再有Segment,但ConcurrentHashMap的并发度仍然受数组长度等因素影响,数组长度在一定程度上决定了并发操作的粒度。

高并发场景下的优化机制

  1. 锁分段技术(JDK 1.7及以前):通过将数据分成多个段,不同线程可以同时访问不同段的数据,减少锁竞争。例如,一个线程在更新Segment[0]的数据时,另一个线程可以同时读取或更新Segment[1]的数据,大大提高了并发性能。
  2. 减小锁粒度(JDK 1.8):在JDK 1.8中,当链表长度超过一定阈值(默认为8)时,链表会转换为红黑树,以提高查找效率。并且,在插入和删除操作时,采用CASSynchronized结合的方式,对单个节点进行操作,而不是像JDK 1.7那样对整个Segment加锁,进一步减小了锁的粒度,提高了并发性能。
  3. 读操作无锁化:无论是JDK 1.7还是JDK 1.8,ConcurrentHashMap的读操作大部分情况下是无锁的。在JDK 1.7中,Segment中的数据通过volatile关键字修饰,保证了可见性,读操作无需加锁。在JDK 1.8中,数组和节点中的数据同样通过volatile修饰,并且通过Unsafe类的getObjectVolatile等方法实现高效的无锁读操作,提高了读性能。
  4. 扩容优化:在扩容时,JDK 1.8的ConcurrentHashMap采用了更优化的方式。它会将数据分散到新的数组中,并且允许在扩容过程中,其他线程继续进行读操作。通过使用ForwardingNode来标识正在扩容的节点,当读操作遇到ForwardingNode时,会去新的数组中查找数据,保证了扩容期间的并发性能。