目录
ConcurrentHashMap.transfer()多线程扩容
ConcurrentHashMap.put()过程总结((hash值)& (数组长度-1)确定位置)
ConcurrentHashMap.get()(hash值)& (数组长度-1)确定位置)扩容时可以get】
JDK1.7和JDK1.8ConcurrentHashMap区别:
Concurrent HashMap(数组+链表+红黑树)
ConcurrentHashMap不允许key或value为null值。
重要的属性sizeCtl
- -1代表正在初始化
- -N 表示有N-1个线程正在进行扩容操作
- 0代表hash表还没有被初始化
- >0 代表扩容阈值,它的值始终是当前ConcurrentHashMap容量的0.75倍
数组table中节点类型和Hash值关系(区分节点的类型就是用Hash值区分的):
- Node----》Hash值>=0
- ForwardingNod-----》用Hash值=MOVED=-1标记
- TreeBin ------》用Hash值=TREEBIN=-2,TreeBin里是TreeNode集合,是红黑树结构
- ReservationNode:数组中占位临时节点----》Hash值=RESERVED=-3
ForwardingNode的情况(表示扩容时处理完毕的节点)
- 扩容时,老数组某个位置没有元素,则标记为ForwardingNode节点(此时无需处理所以直接标记处理完毕)
- 扩容时,已迁移完毕,则标记为ForwardingNode节点
初始化方法
初始化方法主要应用了关键属性sizeCtl 如果这个值〈0,表示其他线程正在进行初始化,就放弃这个操作。如果获得了初始化权限,就用CAS方法将sizeCtl置为-1,防止其他线程进入。
初始化:默认初始化容量为16,加载因子为0.75的数组。将sizeCtl的值改为0.75*n作为触发扩容的阈值
ConcurrentHashMap.transfer()多线程扩容
基本认识:
每个线程扩容时,根据cpu数量确定要扩容的步长,cpu数量越多,那每个线程需要处理的步长越小,最小步长为16
假设原数组长度为64:
扩容时transferIndex记录了下一个线程需要处理的最大边界,
每当来一个线程来帮助扩容时,范围计算公式:[transferIndex-计算出的步长,transferIndex)
transferIndex的初始值=数组长度=n
第一个参与扩容的线程,[64