HashMap 线程不安全,在多线程的情况下,可能会造成死循环,导致cpu 利用率低下
HashTable 线程安全,但是效率低下,使用synchronized 保证线程安全(put /get 都有锁)
解决办法
HashTable 容器在竞争激烈的情况出现低效率的原因是:所有访问HashTable 的线程都竞争同一把锁,假如容器有多把锁,每一把锁用于锁容器中一部分数据,那么多线程访问容器里不同段的数据时,线程间就不会存在竞争锁。
可以有效的提高效率,ConcurrentHashMap 使用的就是锁分段技术。
ConcurrentHashMap 是由 Segment 数组结构和 HashEntity 数组结构组成。Segment 是一种可重入锁 ReentrantLock ,在ConcurrentHashMap 里扮演锁的角色,HashEntity 则用于存储键值对数据。一个ConcurrentHashMap 里包含
一个Segment 数组,Segment 的结构与hashMap 类似,是一种数组和链表结构。一个Segment 里包含一个HashEntity 数组,每隔HashEntity 是一个链表结构的元素,每个Segment 守护着一个HashEntity 数组里的元素,当对HashEntity
数组进行修改时,必须首先获得它对应的Segment 锁。
JDK 1.8 的实现抛弃了Segment 分段锁机制,采用了 CAS + Synchronized 来保证并发更新的安全。数据结构采用:数组+链表+红黑树
初始化参数
private static final int MAXIMUM_CAPACITY = 1 << 30; private static final int DEFAULT_CAPACITY = 16; static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private static final float LOAD_FACTOR = 0.75f; static final int TREEIFY_THRESHOLD = 8; static final int UNTREEIFY_THRESHOLD = 6;
table 扩容
当table 容量不足时,需要进行扩容,整个扩容分为两部分:
- 构建一个nextTable ,大小为table 的两倍
- 把table 的数据复制到nextTable 中