简单说说ConcurrentHashMap的结构和实现

ConcurrentHashMap是线程安全的HashMap,并非通过锁住整个方法,而是在方法内进行一些原子的操作和局部加锁保证多线程的安全

一些些随机

ConcurrentHashMap 内部是以Node形式来存储的

transient volatile Node<K,V>[] table;

这里使用volatile也是保证了可见性和禁止重排序

在ConcurrentHashMap的pu方法中会使用CAS来保证原子性

在1.7和1.8中的扩容实现

ConcurrentHashMap的数据结构如下:

  • ConcurrentHashMap由多个Segment组成,每个Segment都是一个独立的哈希表。
  • 每个Segment维护了一个数组节点的数组,每个节点是一个链表或者树结构,用于存储键值对的数据。
  • 每个节点包含了键、值和指向下一个节点的引用,当节点的数量达到一定阈值时,链表会转换为树结构,以提高查询性能。
  • ConcurrentHashMap使用分段锁的机制以保证线程安全性,每个Segment都维护了一个独立的锁。这样不同线程在访问不同的Segment时可以并发进行操作,提高了并发性能。

ConcurrentHashMap的扩容方式如下:

  • 为了提高并发操作的效率,ConcurrentHashMap在初始化时会创建一个固定大小的Segment数组。每个Segment有一个独立的锁,对应着一部分键值对。
  • 当哈希表中的节点数量达到一定的阈值(默认是数组长度的75%)时,会触发扩容操作。
  • 扩容的流程是将每个Segment中的节点抽取出来,重新计算哈希值并放入新的扩容数组中。扩容数组的长度是原数组长度的两倍。
  • 在扩容过程中,不会对整个哈希表进行加锁,只对正在进行扩容操作的Segment进行加锁,其他Segment仍然可以读写操作。
  • 扩容过程中,可能会出现并发冲突,需要进行适当的锁操作和数据迁移,以保证线程安全性。

1.8

数据结构:
在JDK 1.8中,ConcurrentHashMap的内部数据结构发生了重大改变。JDK 1.7中的分段锁机制被替换为基于CAS(Compare and Swap)操作的Node数组和红黑树结构,以提供更好的并发性能和可扩展性。

  1. 数组:ConcurrentHashMap使用一个Node数组来存储键值对。每个数组中的元素称为Node节点。每个节点包含了键、值、哈希值,以及指向下一个节点的引用。

  2. 链表和红黑树:JDK 1.8中的ConcurrentHashMap使用了链表和红黑树来解决哈希冲突的问题。当链表的长度达到一定阈值(默认为8)时,链表会自动转换为红黑树,以提高查找和删除操作的效率。

扩容方式:
JDK 1.8中的ConcurrentHashMap使用了一种优化的扩容方式,被称为树化分割(TreeNode splitting)。

  1. 初始容量:和JDK 1.7一样,ConcurrentHashMap在初始化时会创建一个固定大小的Node数组,长度为16。

  2. 扩容阈值:JDK 1.8中的ConcurrentHashMap的扩容阈值相较于JDK 1.7有所不同。扩容阈值设置为容量的3/4,并且在每次扩容后会翻倍。也就是说,当哈希表的节点数量达到阈值时就会触发扩容操作。

  3. 扩容过程:当哈希表需要扩容时,JDK 1.8中的ConcurrentHashMap采用了树化分割的方式。

    • 首先,会将数组中的每一个节点重定位到新的数组中。
    • 然后,对于链表和红黑树结构中的每个节点,会将其拆分为两个节点,一个节点仍然保留在原来的位置,另一个节点则被移动到新数组的对应位置上。

通过树化分割的方式,JDK 1.8中的ConcurrentHashMap在进行扩容时可以更高效地转移数据,并提供更好的并发性能。此外,JDK 1.8中的ConcurrentHashMap还引入了一些改进,如在读操作上引入了乐观锁机制,以进一步提高并发性能。

你可能感兴趣的:(面试准备,spring)