ConcurrentHashMap put/get/扩容操作
1.put操作
- 计算key的hash值
- 如果table为null,则初始化table
- 如果当前正在扩容,则会协助扩容操作,然后再重新执行put操作
- 如果table对应槽位为空,则通过CAS操作插入节点。如果CAS成功,则插入成功;如果CAS失败,则表明其它线程已经插入了节点,则继续后续的操作
- 如果对应位置不为空,则使用
synchronized
锁住该桶的第一个节点,然后遍历链表或者红黑树,在链表或红黑树中执行插入或更新操作 - 插入后,如果链表的长度超过了8,且数组的长度>=64,则将链表转化为红黑树
- 插入完成后,判断是否需要扩容。若需要,则扩容
2.get操作
get操作不需要加锁,实际存储数据的Node节点的value字段和next字段都是通过volatile
修改的,保证了可见性,所以可以做到无锁并发读
3.扩容操作
当table的元素数量达到阈值时,需要进行扩容。ConcurrentHashMap支持多线程并发进行,扩容时会给不同的线程分配桶区间来并发扩容。扩容期间,如果有其它的线程在执行put操作,则会帮助进行迁移。
迁移过程中,首先会使用synchronized锁住桶的头节点,然后进行迁移。迁移完成后,将原数组的该桶位置设置为ForwardingNode,表示已迁移(其它线程put时,如果发现是forwardingNode,则会转而协助扩容)。迁移时,将桶中的节点根据hash值重新计算在新数组中的位置。由于新数组容量是原数组的2倍,所以每个节点在新数组中的位置要么是原位置,要么是原位置+原数组长度。