前言
之前我们阅读了HashMap等集合框架的源码,这次我们把视线转移到java.util.concurrent上,探索下包括ConcurrentHashMap在内的JUC框架的奥秘。
正文
1、常量
首先,我们介绍下ConcurrentHashMap中的常量:
/**
* 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;
table可能出现的最大容量。为了保持在table大小是2次方时进行Java数组分配和索引限定,这个值必须是1<<30,由于32位hash散列中的前两位被用作控制目的,这个值是更加需要的。
/**
* 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;
默认的初始table容量。必须是2的次方(即最小为1),最大是1 << 30;
/**
* The largest possible (non-power of two) array size.
* Needed by toArray and related methods.
*/
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
可能出现的最大数组大小(不是2的次方)。在toArray和相关方法中使用到。
/**
* The default concurrency level for this table. Unused but
* defined for compatibility with previous versions of this class.
*/
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
当前table默认的并发水平。在构建这个类的以前兼容版本时用到。
/**
* The load factor for this table. Overrides of this value in
* constructors affect only the initial table capacity. The
* actual floating point value isn't normally used -- it is
* simpler to use expressions such as {@code n - (n >>> 2)} for
* the associated resizing threshold.
*/
private static final float LOAD_FACTOR = 0.75f;
当前table的负载因子。仅在初始化table容量时在构造器中可以覆盖当前值。这个实际浮点值未得到正常使用–对相关的扩容阈值来说,它更简单的使用如(n >>> 2)的代码。
/**
* 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;
使用树(而不是列表)来设置bin计数阈值。当向至少具有这么多节点的bin添加元素时,bin将转换为树。该值必须大于2,并且应该至少为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;
用于在调整大小操作期间反树化(拆分)bin的bin计数阈值。应小于TREEIFY_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;
最小的表容量,其中的箱子可以treeified。(否则,如果一个bin中有太多节点,则会调整表的大小。)该值应该至少为4 * TREEIFY_THRESHOLD,以避免调整大小和treeification阈值之间的冲突。
/**
* Minimum number of rebinnings per transfer step. Ranges are
* subdivided to allow multiple resizer threads. This value
* serves as a lower bound to avoid resizers encountering
* excessive memory contention. The value should be at least
* DEFAULT_CAPACITY.
*/
private static final int MIN_TRANSFER_STRIDE = 16;
每个转移步骤的最少复归数。范围被细分以允许多个调整大小的线程。此值用作下限,以避免调整大小器遇到过多的内存争用。该值至少应该是DEFAULT_CAPACITY。
/**
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
*/
private static int RESIZE_STAMP_BITS = 16;
用于生成戳记的位的数目,单位为sizeCtl。32位数组必须至少为6。
/**
* The maximum number of threads that can help resize.
* Must fit in 32 - RESIZE_STAMP_BITS bits.
*/
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
可以帮助调整大小的最大线程数。必须符合32 - RESIZE_STAMP_BITS位。
/**
* The bit shift for recording size stamp in sizeCtl.
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
用sizeCtl记录尺寸戳的位偏移。
/*
* Encodings for Node hash fields. See above for explanation.
*/
static final int MOVED = -1; // hash for forwarding nodes
static final int TREEBIN = -2; // hash for roots of trees
static final int RESERVED = -3; // hash for transient reservations
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
节点哈希字段的编码。见上面的解释。
2、存储单元
接下来,我们看看ConcurrentHashMap内部的存储单元,Node<K,V>:
/**
* Key-value entry. This class is never exported out as a
* user-mutable Map.Entry (i.e., one supporting setValue; see
* MapEntry below), but can be used for read-only traversals used
* in bulk tasks. Subclasses of Node with a negative hash field
* are special, and contain null keys and values (but are never
* exported). Otherwise, keys and vals are never null.
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return val; }
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final String toString(){ return key + "=" + val; }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* Virtualized support for map.get(); overridden in subclasses.
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
可以看出,这是一个键值对存储单元,并且还有一个指向下一个节点的指针,即一个链表结构。
3、成员属性
最后,我们看下ConcurrentHashMap的成员属性:
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node<K,V>[] table;
箱子的数组。在第一次插入时惰性初始化。大小总是2的幂。由迭代器直接访问。
/**
* The next table to use; non-null only while resizing.
*/
private transient volatile Node<K,V>[] nextTable;
要使用的下一个表;仅在调整大小时非空。
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
*/
private transient volatile long baseCount;
基本计数器值,主要在没有争用时使用,也可作为表初始化竞争期间的回退。通过CAS机制更新。
/**
* 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;
表初始化和调整大小控件。当为负值时,将初始化或调整表的大小:-1表示初始化,否则-(1 +活动调整大小的线程数)。否则,当表为空时,保留创建时使用的初始表大小,默认情况下为0。初始化之后,保存下一个元素count值,根据该值调整表的大小。
/**
* The next table index (plus one) to split while resizing.
*/
private transient volatile int transferIndex;
调整大小时要分割的下一个表索引(加上一个)。
/**
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
*/
private transient volatile int cellsBusy;
自旋锁(通过CAS锁定),用于调整大小和/或创建反单元格。
/**
* Table of counter cells. When non-null, size is a power of 2.
*/
private transient volatile CounterCell[] counterCells;
计数器单元格表。当非空时,size是2的幂。
在接下来的文章中,我们将通过测试案例来探索put方法中蕴含哪些不为人知的秘密。