ConcurrentHashMap的实现原理是什么

参考答案

HashMap是线程不安全、效率高,HashTable是线程安全、效率低。

ConcurrentHashMap可以做到既是线程安全的,同时也可以有很高的效率,得益于使用了分段锁。

ConcurrentHashMap的实现原理:

1.  JDK 1.7

  • ConcurrentHashMap是通过数组 + 链表实现,由Segment数组和Segment元素里对应多个HashEntry组成;
  • value 和链表都是volatile修饰,保证可见性;
  • ConcurrentHashMap采用了分段锁技术,分段指的就是Segment数组,其中Segment继承于ReentrantLock;
  • 理论上ConcurrentHashMap支持CurrencyLevel (Segment 数组数量)的线程并发,每当一个线程占用锁访问一个Segment时,不会影响到其他的Segment。

1.1  put 方法的逻辑较复杂

  • 尝试加锁,加锁失败scanAndLockForPut方法自旋,超过MAX_SCAN_RETRIES次数,改为阻塞锁获取;
  • 将当前Segment中的table通过key的hashcode定位到HashEntry;
  • 遍历该HashEntry,如果不为空则判断传入的 key 和当前遍历的key是否相等,相等则覆盖旧的value;
  • 不为空则需要新建一个HashEntry并加入到Segment中,同时会先判断是否需要扩容;
  • 最后释放所获取当前Segment的锁。

1.2  get 方法较简单

  • 将 key 通过hash之后,定位到具体的Segment,再通过一次hash定位到具体的元素上;
  • 由于 HashEntry中的value属性是用volatile关键词修饰的,保证了其内存可见性。

2.  JDK 1.8

  • 抛弃了原有的Segment分段锁,采用了CAS + synchronized来保证并发安全性;
  • HashEntry改为Node,作用相同;
  • val next 都用了volatile修饰。

2.1  put 方法逻辑

  • 根据 key 计算出hash值;
  • 判断是否需要进行初始化;
  • 根据key定位出的Node,如果为空表示当前位置可以写入数据,利用CAS尝试写入,失败则自旋;
  • 如果当前位置的hashcode==MOVED== -1,则需要扩容;
  • 如果都不满足,则利用synchronized锁写入数据;
  • 如果数量大于TREEIFY_THRESHOLD则转换为红黑树。

2.2  get 方法逻辑

  • 根据计算出来的hash值寻址,如果在桶上直接返回值;
  • 如果是红黑树,按照树的方式获取值;
  • 如果是链表,按链表的方式遍历获取值。

3. JDK 1.7 到 JDK 1.8 中的 ConcurrentHashMap 最大的改动

  • 链表上的 Node 超过 8 个改为红黑树,查询复杂度 O(logn)
  • ReentrantLock 显示锁改为 synchronized,说明 JDK 1.8 中 synchronized 锁性能赶上或超过 ReentrantLock

以上,是Java面试题【ConcurrentHashMap的实现原理是什么】的参考答案。

输出,是最好的学习方法

欢迎在评论区留下你的问题、笔记或知识点补充~

—end—

👇阅读作者更多技术干货👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧