1. 不允许 null 类型的 key 或 value
2. 懒初始化,第一次插入数据才会初始化容量数组
3. 桶中第一个节点的插入基于 CAS,其他基于 lock(lock 第一个节点),新节点追加到尾部
锁粒度相比 Segment 更细了,每一个槽都可以独立上锁
4. Tree 使用 hashcode 比较大小,如果可能使用 Comparable.compareTo 比较
5. 最大容量 2^30,其他位用于控制
/**
* The largest possible table capacity. This value must be
* exactly 1<<30 to stay within Java array allocation and indexing
* bounds for power of two table sizes, and is further required
* because the top two bits of 32bit hash fields are used for
* control purposes.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
6. 默认容量 16 ,负载因子 0.75,与 HashMap 一样
7. 同样支持 链表 和 树 结构,与 HashMap 一样
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2, and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* The value should be at least 4 * TREEIFY_THRESHOLD to avoid
* conflicts between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64;
8. 用到了 sun.misc.Unsafe 实现的 compareAndSwapObject 方法,即 CAS
9. 状态判断控制变量
/**
* 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;
并发扩容总结
- 单线程新建nextTable,扩容为原table容量的两倍。
- 每个线程想增/删元素时,如果访问的桶是ForwardingNode节点,则表明当前正处于扩容状态,协助一起扩容完成后再完成相应的数据更改操作。
- 扩容时将原table的所有桶倒序分配,每个线程每次最小分16个桶进行处理,防止资源竞争导致的效率下降, 每个桶的迁移是单线程的,但桶范围处理分配可以多线程,在没有迁移完成所有桶之前每个线程需要重复获取迁移桶范围,直至所有桶迁移完成。
- 一个旧桶内的数据迁移完成但迁移工作没有全部完成时,查询数据委托给ForwardingNode结点查询nextTable完成(这个后面看find()分析)。
- 迁移过程中sizeCtl用于记录参与扩容线程的数量,全部迁移完成后sizeCtl更新为新table的扩容阈值。