JUC-ConcurrentHashMap

94 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

ConcurrentHashMap是面试常客了。

Concurrent 类型的容器

  • 内部很多操作使用 cas 优化,一般可以提供较高吞吐量
  • 弱一致性
    • 遍历时弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历,但是旧数据
    • 求大小弱一致性,size 操作未必是 100% 准确

为什么会有ConcurrentHashMap

因为HashMap是线程不安全的,而使用 HashTable 虽然可以保证线程安全问题,但其是采用的是 synchronized 锁将整个 HashTable 中的数组锁住, 在多个线程中只允许一个线程访问 Put 或者 Get,效率低。

而且多线程下HashMap有两个问题:

  • 循环死链问题:JDK1.7之前头插法导致的,后面1.8改为尾插法了。
  • 值覆盖问题:多线程计算索引是同一个位置,然后前一个的put操作被后一个put操作给覆盖了

于是多线程的情况下 JDK 官方推荐使用 ConcurrentHashMap

ConcurrentHashMap和HashTable的区别

实现线程安全的方式(重要):

① 在 JDK1.7 的时候,ConcurrentHashMap采用分段锁设计,对整个桶数组进行了分段设计,切分为多个segment,再把小数组分为多个hashEntry。 有种从空间上细化锁粒度的感觉

到了 JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,通过使用 synchronized 和 CAS 来完成并发控制。

② Hashtable:只是采用 数组+链表 的形式,都是在方法上使用 synchronized 来保证线程安全,效率非常低下。

ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?

因为多线程操作下,会有二义性

二义性是指,不确定是key为null还是value为null

单线程的hashmap可以通过containsKey做判断,而多线程下的concurrentHashmap可能会同时发生put操作,所以不支持key为null