Understanding ConcurrentHashMap Constructors, Insertion, Treeification, and Resizing in Java
This article explains the internal mechanisms of Java's ConcurrentHashMap, covering its constructors, element insertion via put and putVal, the sizeCtl variable, treeification of bins, counting operations, and the complex multi‑threaded resizing process, with detailed code examples.
The article provides an in‑depth analysis of Java's ConcurrentHashMap implementation, starting with its constructors: a default constructor that creates an empty map with an initial array size of 16, and a constructor that accepts an initial capacity and computes the next power‑of‑two size.
/**
* 默认的构造方法为空,不做任何操作,数组长度默认是16
*/
public ConcurrentHashMap() {
}
/**
* 传递初始化容量的构造方法,传递进来一个初始容量,
* ConcurrentHashMap会基于这个值计算一个比这个值大的2的幂次方数作为初始容量
* 与其他版本不同,例如:传递 16 作为参数,它会计算得到 32 作为初始化容量,而不是 16
*/
public ConcurrentHashMap(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException();
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}It then discusses the sizeCtl variable, which controls initialization, resizing thresholds, and indicates the state of the table (uninitialized, initializing, resizing, or normal operation). The meanings of different sizeCtl values are enumerated.
1. sizeCtl 为 0,代表数组未初始化,且数组的初始容量为16
2. sizeCtl 为正数,如果数组未初始化,那么其记录的是数组的初始容量,如果数组已经初始化,那么其记录的是数组的扩容阈值
3. sizeCtl 为 -1,表示数组正在进行初始化
4. sizeCtl 小于 0,并且不是 -1,表示数组正在扩容, -(1+n),表示此时有 n 个线程正在共同完成数组的扩容操作The put() method delegates to putVal() , which contains the core insertion logic handling null checks, hash computation, table initialization, direct CAS insertion into empty bins, assistance with ongoing resizing, and synchronized handling of non‑empty bins. It details how entries are added to linked lists, tree bins, and how bin counts trigger treeification when they exceed TREEIFY_THRESHOLD (8).
/**
* put() 方法会默认调用 putVal() 方法做具体添加元素逻辑
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
/**
* putVal() 方法做具体添加元素逻辑。根据不同的条件进行不同的插入方案选择。
* 大致分为 直接cas插入,链表插入,树插入以及协助扩容等操作。
* 添加完毕后需要对数组元素个数更新,并且判别是否需要扩容。
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
// ... (implementation omitted for brevity)
}The article also covers the initTable() method, which safely initializes the table using CAS and spin‑wait loops, setting the initial capacity and the resize threshold (75% of the table size).
/**
* initTable()方法采用 CAS+自旋的方式线程安全的初始化数组
*/
private final Node
[] initTable() {
// ... (implementation omitted for brevity)
}Treeification is explained via treeifyBin() , which converts a bin's linked list into a red‑black tree when the list length reaches the threshold and the table size is at least MIN_TREEIFY_CAPACITY (64). The construction of TreeBin and its root node is shown.
/**
* treeifyBin()方法。首先对数组长度判别,查看是否直接扩容数组即可。
* 之后若满足树化条件,则进行树化的步骤,进行双向链表构建,并进行 TreeBin 对象的构建。
*/
private final void treeifyBin(Node
[] tab, int index) {
// ... (implementation omitted for brevity)
}
/**
* TreeBin 的构造方法为具体构建红黑树的逻辑。
*/
TreeBin(TreeNode
b) {
super(TREEBIN, null, null, null);
this.first = b;
// ... (tree construction omitted)
}Counting operations are handled by addCount() , fullAddCount() , and sumCount() , which use a striped CounterCell array similar to LongAdder to reduce contention when updating the map size.
/**
* addCount() 方法主要包括两个功能:
* 1.记录ConcurrentHashMap元素数量,会调用fullAddCount具体执行
* 2.扩容ConcurrentHashMap,会调用transer方法具体执行扩容
*/
private final void addCount(long x, int check) {
// ... (implementation omitted for brevity)
}
/**
* fullAddCount() 方法用于将需要添加的个数累加到baseCount上,
* 或者累加到其他CountCell数组中的每个对象中的value属性上
*/
private final void fullAddCount(long x, boolean wasUncontended) {
// ... (implementation omitted for brevity)
}
/**
* sumCount() 方法会聚合所有累加单元和baseCount的值
*/
final long sumCount() {
// ... (implementation omitted for brevity)
}Finally, the resizing mechanism is detailed through transfer() , which performs the actual migration of entries to a new table, and helpTransfer() , which allows other threads to assist in the resizing process. The algorithm uses CAS, spin‑wait, and a cooperative thread count to ensure safe and efficient table expansion.
/**
* ConcurrentHashMap触发扩容的时机与HashMap类似,
* 要么是在将链表转换成红黑树时判断table数组的长度是否小于阈值(64),
* 如果小于就进行扩容而不是树化,要么就是在添加元素的时候,
* 判断当前Entry数量是否超过阈值,如果超过就进行扩容。
*/
private final void transfer(Node
[] tab, Node
[] nextTab) {
// ... (implementation omitted for brevity)
}
/**
* helpTransfer() 方法是用于其他线程协助扩容的功能。
*/
final Node
[] helpTransfer(Node
[] tab, Node
f) {
// ... (implementation omitted for brevity)
}The article concludes with a brief promotional note unrelated to the technical content.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.