【Java 快速复习】理解 HashMap & ConcurrentHashMap

理解 HashMap & ConcurrentHashMap

首先是 HashMap , HashMap 本质上是数组实现的,思想是通过对 key 计算 hashCode 并将其 hashCode % (array.length - 1) 来计算要放到哪个位置。

Java 在设计 HashMap 的时候是这样设计的:

  1. Node<K,V>[] table; 数组承载数据
  2. 限制数组大小必须为 2^n 并基于此优化计算槽位算法为位运算 hash & (length -1)
  3. hash 值高低位扰动,增加随机性降低 hash 冲突概率
  4. 出现哈希冲突时进行拉链,即 数组 + 链表 (1.8 后引入红黑树,当链表大于 8 个节点时会将结构优化为红黑树加快查询效率 logN)
  5. 负载因子 0.75 ,即数组内元素到达数组长度 75% 时进行扩容 (1.7 版本的扩容头插法实现如果在有链表且存在并发扩容的情况有可能会引发链表成环的问题,在 1.8 版本进行了优化改为高低位指针解决了此问题。)

注意 HashMap 线程不安全

线程安全的 HashMap -> HashTable -> ConcurrentHashMap

提一下 HashTable ,其为了线程安全是直接暴力加锁,其 get 和 put 都会直接对当前对象加 synchronized 锁,这个效率是很低的。

所以生产环境基本不会用。

对于 ConcurrentHashMap Java 是这样设计的

  1. 分段锁机制
    1. 1.7 版本 采用的是 二次哈希,大的数组中套小数组 Segment,大数组 hash 后得到槽位,锁当前槽位,对槽位的小数组再进行 hash 最后入表
    2. 1.8 版本的实现基本和 HashMap 一样,在写数据时采用了 自旋 + cas 的方式,也就是说只有当前槽位有数据时才会进行加锁
  2. 扩容
    1. 1.7 类似 HashMap
    2. 1.8 版本利用了多线程的优势,渐进式扩容,会有新旧两个 table ,触发扩容后会扩容部分数据,旧数据会变为 ForwardingNode 且 hash = -1 moved 标记为已移动,如果其他线程在 put 或 remove 时命中了 ForwardingNode 节点会帮助进行扩容,分配扩容区域 ,最少分配 16 个槽位的迁移。

猜你喜欢

转载自blog.csdn.net/w903328615/article/details/129171652