ConcurrentHashMap之initTable
前提
- ConcurrentHashMap是线程安全的
- ConcurrentHashMap数据结构:数组+hash算法+链表(jdk1.8之前)+树结构(jdk1.8以后)
- 当前jdk版本1.8
作用
- map初始化,设置table大小
业务逻辑
- 1,如果当前table数组是空的,进入初始化,否则table,不需要初始化
- 2,sizeCtl的意思,当前table是否正在初始化,如果是,则让出线程
- 3,开始进入初始化,并修改sizeCtl的值,防止其他线程初始化,原子层面修改sizeCtl值,可以快速修改,并同步数据
- 修改sizeCtl的值为-1
- 开始初始化大小
- 初始化结束,修改sizeCtl的值为sc,默认清空下sizeCtl的值为12
代码
initTable
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
/**
* The default initial table capacity. Must be a power of 2
* (i.e., at least 1) and at most MAXIMUM_CAPACITY.
*/
private static final int DEFAULT_CAPACITY = 16;
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//1,如果当前table数组是空的
while ((tab = table) == null || tab.length == 0) {
//2,sizeCtl是否有线程占用,如果是,则让出CPU,进入就绪状态,默认值为0,-1指的是正在初始化
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//3,修改sc的值为-1,原子层面修改,compareAndSwapInt本地方法,具体如何实现,可以下载相关源码查看
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//如果 sizeCtl>0 初始化大小为sizeCtl,否则初始化大小为16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//sc赋值,如果n为16,则sc = 16-16/4 = 12,
sc = n - (n >>> 2);
}
} finally {
//赋值给sizeCtl,初始化结束,sizeCtl的值>0
sizeCtl = sc;
}
break;
}
}
return tab;
}
sizeCtl的作用
- 在多线程的情况下,多个线程同时初始化同一个table,此时使用sizeCtl进行标识,初始化的开始,初始化中,初始化结束的状态,防止多次初始化
compareAndSwapInt的作用
- 底层方法实现
- 主要作用是修改sc的值,以达到防止其他线程同时初始化
- 后期会把compareAndSwapInt相关的源码贴出来