千锋教育2023版Java面试宝典Java面试200题(含美团、字节、阿里大厂真

在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的HashTable效率又非常低下,基于以上两个原因,便有了ConcurrentHashMap的登场机会
- 线程不安全的HashMap
多线程环境下会导致HashMap的 Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,调用.next() 时就会产生死循环获取Entry。
- 效率低下的HashTable
HashTable是使用 synchronized来保证线程安全,当线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,读写操作均需要获取锁。
- 分段锁的ConcurrentHashMap
在ConcurrentHashMap中,将数据分成一段一段地存储,然后给每段数据都配一把锁,当线程占用锁访问其中一段数据的时候,其他段的数据也能被被其他线程访问。
- ConcurrentHashMap的几个特点
- 默认数组大小为16
- 扩容因子为0.75,扩容后数组大小翻倍
- 当存储的node总数量>= 数组长度“扩容因子时,会进行扩容 (数组中的元素、链表元素、红黑树元素都是内部类Node的实例或子类实例,这里的node总数量是指所有put进map的node数量)
- 当链表长度>=8且数组长度<64 时会进行扩容
- 当数组下是链表时,在扩容 的时候会从链表的尾部开始rehash
- 当链表长度>=8且数组长度>=64时链表会变成红黑树
- 树节点减少直至为空时会将对应的数组下标置空,下次存储操作再定位在这个下标t时会按照链表存储
- 扩容时树节点数量<=6时会变成链表
- 当一个事物操作发现map正在扩容时,会帮助扩容
- map正在扩容时获取 (get等类似操作)操作还没进行扩容的下标会从原来的table获取扩容完毕的下标会从新的table中获取