/**
* 如果指定的值key还没有关联到一个value,那么尝试使用给定的函数计算它的值,
* 如果值不为null,则将它放入此map中
* If the specified key is not already associated with a value,
* attempts to compute its value using the given mapping function
* and enters it into this map unless {@code null}. The entire
* 整个方法调用是原子执行的,因此这个函数每个key最多使用一次。
* method invocation is performed atomically, so the function is
* applied at most once per key. Some attempted update operations
* 在计算过程中可能会阻止其他线程对该map进行的某些尝试更新操作,因此计算应该简短并且简单,
* 不得尝试更新此map的任何其他映射。
* on this map by other threads may be blocked while computation
* is in progress, so the computation should be short and simple,
* and must not attempt to update any other mappings of this map.
*
* @param key key with which the specified value is to be associated
* @param mappingFunction the function to compute a value
* @return the current (existing or computed) value associated with
* the specified key, or null if the computed value is null
* @throws NullPointerException if the specified key or mappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
*/
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
// 判空
if (key == null || mappingFunction == null)
throw new NullPointerException();
// 计算key 的哈希值
int h = spread(key.hashCode());
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
// 如果key 对应的索引位置上还没有元素
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
// todo : 为什么不使用CAS把值放入
// -->不直接使用CAS把目标node放入的原因是:使用给定函数计算目标值可能是一个比较耗时的过程,一旦发现该
// 索引位置上没有元素,那么先创建一个ReservationNode,并使用它作为该索引位置的锁,然后获取锁,再使
// 用CAS设置进去,占住该索引位置,然后再利用给定的函数,计算其值,计算完成了再创建目标node,然后替换
// 为目标node。否则CAS失败了还得判断是否存在这个元素,然后继续处理
Node<K,V> r = new ReservationNode<K,V>();
// 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
// CAS成功后,binCount值设置为1
binCount = 1;
Node<K,V> node = null;
try {
// 使用给定的函数计算值,参数传入key
if ((val = mappingFunction.apply(key)) != null)
node = new Node<K,V>(h, key, val, null);
// 如果计算的值为null,那么什么都不做
} finally {
// 1、如果异常了,那么会把该索引位置上的值设置回null
// 2、如果没有异常,那么会把该索引位置上的值设置为目标node
// ReservationNode 会被覆盖掉
setTabAt(tab, i, node);
}
}
}
// binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
// 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
if (binCount != 0)
break;
}
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
boolean added = false;
// 先获取锁
synchronized (f) {
// 获取到锁后还需要判断,该索引位置的第一个元素是否还是f
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek; V ev;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// key 已存在,获取key 映射的值
val = e.val;
break; // 结束循环
}
Node<K,V> pred = e;
// e = e.next, e.next = null说明该索引位置上没有其他元素了
if ((e = e.next) == null) {
// 使用给定的函数计算值,参数传入key
if ((val = mappingFunction.apply(key)) != null) {
added = true;
// 插入数据
pred.next = new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
// break 退出循环,binCount不会再自增
break;
}
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
// findTreeNode()查找key对应的节点,没有返回null
// 如果 p != null,说明key 已经存在
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null)
// 获取key 映射的值
val = p.val;
// 如果key不存在,使用给定的函数计算值,参数传入key
else if ((val = mappingFunction.apply(key)) != null) {
added = true;
t.putTreeVal(h, key, val);
}
}
}
}
// 如果该索引位置在插入之前已有元素,那么binCount 一定不为0,但是 tabAt(tab, i) == f值为false的
// 时候binCount的值为0,而added的值是false,所以这里必须要判断 binCount != 0
if (binCount != 0) {
// TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
// 9个才会转成红黑树。HashMap的computeIfAbsent()元素个数达到8个就转成红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
// added = false 说明key在之前已经存在,直接返回原来的值,元素个数不需要 +1
if (!added)
return val;
break;
}
}
}
if (val != null)
// 元素个数 + 1
addCount(1L, binCount);
// 返回原来的值/新的值
return val;
}
/**
* If the value for the specified key is present, attempts to
* compute a new mapping given the key and its current mapped
* value. The entire method invocation is performed atomically.
* Some attempted update operations on this map by other threads
* may be blocked while computation is in progress, so the
* computation should be short and simple, and must not attempt to
* update any other mappings of this map.
*
* @param key key with which a value may be associated
* @param remappingFunction the function to compute a value
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or remappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
// 判空
if (key == null || remappingFunction == null)
throw new NullPointerException();
// 计算key 的hash值
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
// 如果该索引位置上还没有元素,那么结束循环
else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
break;
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
val = remappingFunction.apply(key, e.val);
if (val != null)
// 如果值不为null,替换原来的值
e.val = val;
else {
// 如果计算出新的值为null,那么删除这个节点
delta = -1;
// Node 只有next,没有prev
Node<K,V> en = e.next;
if (pred != null)
// pred.next 指向删除节点的下一个节点
pred.next = en;
else
// 如果删除的节点是该索引位置上的第一个节点,那么修改
// 第一个节点为: e.next
setTabAt(tab, i, en);
}
break;
}
pred = e;
// 该索引位置上没有下一个元素了,break
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容。 在这里的主要作用就是下面的判断: if (binCount != 0) break;
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null) {
// p != null 说明 key已存在此map中
// 使用给定的函数计算新的值,参数传入 key,原来的值
// 这里把原来的值传进去了,因此可以很容易地实现在原来的值上做加减
val = remappingFunction.apply(key, p.val);
if (val != null)
// 如果新的值不为null,那么替换原来的值
p.val = val;
else {
// 如果新的值为null,那么删除此节点
delta = -1;
// removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (binCount != 0)
break;
}
}
// delta = -1,说明新的值为null,那么会把key所在的节点删除,因此元素个数 -1
if (delta != 0)
addCount((long)delta, binCount);
// 返回新的值
return val;
}
/**
* Attempts to compute a mapping for the specified key and its
* current mapped value (or {@code null} if there is no current
* mapping). The entire method invocation is performed atomically.
* Some attempted update operations on this map by other threads
* may be blocked while computation is in progress, so the
* computation should be short and simple, and must not attempt to
* update any other mappings of this Map.
*
* @param key key with which the specified value is to be associated
* @param remappingFunction the function to compute a value
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or remappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
// 如果key 存在,新的值不为null,那么更新key映射的值为新的值,如果新的值为null,那么删除此节点;
// 如果key 不存在,新的值不为null,那么进行插入,如果新的值为null,那么什么都不做
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
// 判空
if (key == null || remappingFunction == null)
throw new NullPointerException();
// 计算key 的hash值
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
// 如果该索引位置上还没有元素,那么创建一个ReservationNode,把该索引位置占住
Node<K,V> r = new ReservationNode<K,V>();
// 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
// 使用给定的函数计算
if ((val = remappingFunction.apply(key, null)) != null) {
// 如果计算的值不为null,那么插入到map中
delta = 1;
node = new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
} finally {
// 1、如果异常了或者新的值为null,那么会把该索引位置上的值设置回null
// 2、如果没有异常,那么会把该索引位置上的值设置为目标node
// ReservationNode 会被覆盖掉
setTabAt(tab, i, node);
}
}
}
// binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
// 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
if (binCount != 0)
break;
}
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
val = remappingFunction.apply(key, e.val);
if (val != null)
// 如果值不为null,替换原来的值
e.val = val;
else {
// 如果计算出新的值为null,那么删除这个节点
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
// pred.next 指向删除节点的下一个节点
pred.next = en;
else
// 如果删除的节点是该索引位置上的第一个节点,那么修改
// 第一个节点为: e.next
setTabAt(tab, i, en);
}
break;
}
pred = e;
// 该索引位置上没有其他元素了,说明key在此map中不存在,进行插入处理
if ((e = e.next) == null) {
// 使用给定的函数计算值,参数传入key,null
val = remappingFunction.apply(key, null);
if (val != null) {
delta = 1;
// 插入数据
pred.next =
new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
break;
}
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容
binCount = 1;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null)
// findTreeNode()查找key对应的节点,没有返回null
p = r.findTreeNode(h, key, null);
else
// root = null,p 没有初始化,所以初始化为null (不会存在root = null的情况)
p = null;
V pv = (p == null) ? null : p.val;
// 使用给定的函数计算值,参数传入key, pv
val = remappingFunction.apply(key, pv);
if (val != null) {
// p != null,说明key已存在
if (p != null)
// 使用新的值替换原来的值
p.val = val;
else {
// 插入数据
delta = 1;
t.putTreeVal(h, key, val);
}
}
// 新的值 val = null
else if (p != null) {
// p != null,而新的值为null,所以删除此节点
delta = -1;
// removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
// TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
// 9个才会转成红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
// 更新元素个数
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
/**
* If the specified key is not already associated with a
* (non-null) value, associates it with the given value.
* Otherwise, replaces the value with the results of the given
* remapping function, or removes if {@code null}. The entire
* method invocation is performed atomically. Some attempted
* update operations on this map by other threads may be blocked
* while computation is in progress, so the computation should be
* short and simple, and must not attempt to update any other
* mappings of this Map.
*
* @param key key with which the specified value is to be associated
* @param value the value to use if absent
* @param remappingFunction the function to recompute a value if present
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or the
* remappingFunction is null
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
// key 和 value 都不能为null,否则抛出NullPointerException
if (key == null || value == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
delta = 1;
val = value;
break;
}
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(e.val, value);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null) {
delta = 1;
val = value;
pred.next =
new Node<K,V>(h, key, val, null);
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r = t.root;
TreeNode<K,V> p = (r == null) ? null :
r.findTreeNode(h, key, null);
val = (p == null) ? value :
remappingFunction.apply(p.val, value);
if (val != null) {
if (p != null)
p.val = val;
else {
delta = 1;
t.putTreeVal(h, key, val);
}
}
else if (p != null) {
delta = -1;
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
// Hashtable legacy methods
/**
* Legacy method testing if some key maps into the specified value
* in this table. This method is identical in functionality to
* {@link #containsValue(Object)}, and exists solely to ensure
* full compatibility with class {@link java.util.Hashtable},
* which supported this method prior to introduction of the
* Java Collections framework.
*
* @param value a value to search for
* @return {@code true} if and only if some key maps to the
* {@code value} argument in this table as
* determined by the {@code equals} method;
* {@code false} otherwise
* @throws NullPointerException if the specified value is null
*/
public boolean contains(Object value) {
return containsValue(value);
}
/**
* Returns an enumeration of the keys in this table.
*
* @return an enumeration of the keys in this table
* @see #keySet()
*/
public Enumeration<K> keys() {
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
return new KeyIterator<K,V>(t, f, 0, f, this);
}
/**
* Returns an enumeration of the values in this table.
*
* @return an enumeration of the values in this table
* @see #values()
*/
public Enumeration<V> elements() {
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
return new ValueIterator<K,V>(t, f, 0, f, this);
}
// ConcurrentHashMap-only methods
/**
* 返回映射的数量。应该使用此方法代替size(),因为ConcurrentHashMap可能
* 包含比可以表示为int的更多映射。
* Returns the number of mappings. This method should be used
* instead of {@link #size} because a ConcurrentHashMap may
* contain more mappings than can be represented as an int. The
* 返回的值是估计值; 如果并发插入或移除,实际计数可能会有所不同。
* value returned is an estimate; the actual count may differ if
* there are concurrent insertions or removals.
*
* @return the number of mappings
* @since 1.8
*/
public long mappingCount() {
long n = sumCount();
// 忽略瞬时的负值
return (n < 0L) ? 0L : n; // ignore transient negative values
}
/**
* 创建一个新的Set,由一个ConcurrentHashMap支持,使用给定的类型Boolean.TRUE 。
* Creates a new {@link Set} backed by a ConcurrentHashMap
* from the given type to {@code Boolean.TRUE}.
*
* @param <K> the element type of the returned set
* @return the new set
* @since 1.8
*/
// 这是一个静态方法,新创建一个由ConcurrentHashMap支持的Set集合
public static <K> KeySetView<K,Boolean> newKeySet() {
return new KeySetView<K,Boolean>
(new ConcurrentHashMap<K,Boolean>(), Boolean.TRUE);
}
/**
* Creates a new {@link Set} backed by a ConcurrentHashMap
* from the given type to {@code Boolean.TRUE}.
*
* @param initialCapacity The implementation performs internal
* sizing to accommodate this many elements.
* @param <K> the element type of the returned set
* @return the new set
* @throws IllegalArgumentException if the initial capacity of
* elements is negative
* @since 1.8
*/
// 静态方法,新创建一个由 能容纳指定元素数量的ConcurrentHashMap支持的Set集合
// initialCapacity 是 table 可容纳的元素个数,而不是 table 的 size
public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
return new KeySetView<K,Boolean>
(new ConcurrentHashMap<K,Boolean>(initialCapacity), Boolean.TRUE);
}
/**
* 返回这个map中的keys的set视图,对于任何的添加( add()、addAll() )使用
* 给定的公共映射值。
* Returns a {@link Set} view of the keys in this map, using the
* given common mapped value for any additions (i.e., {@link
* Collection#add} and {@link Collection#addAll(Collection)}).
* 当然,这只适用于从这个视图中对所有添加使用相同的值是可接受的
* This is of course only appropriate if it is acceptable to use
* the same value for all additions from this view.
*
* @param mappedValue the mapped value to use for any additions
* @return the set view
* @throws NullPointerException if the mappedValue is null
*/
// 此方法返回的set支持插入元素
public KeySetView<K,V> keySet(V mappedValue) {
// mappedValue 不能为null
if (mappedValue == null)
throw new NullPointerException();
return new KeySetView<K,V>(this, mappedValue);
}
/* ---------------- Special Nodes -------------- */
/**
* A node inserted at head of bins during transfer operations.
*/
static final class ForwardingNode<K,V> extends Node<K,V> {
// 扩容新的table
final Node<K,V>[] nextTable;
// tab -> 扩容时使用的新table
ForwardingNode(Node<K,V>[] tab) {
// hash值为 MOVED
super(MOVED, null, null, null);
this.nextTable = tab;
}
Node<K,V> find(int h, Object k) {
// 循环,以避免转发节点上的任意深度递归
// loop to avoid arbitrarily deep recursion on forwarding nodes
outer: for (Node<K,V>[] tab = nextTable;;) {
// tab = nextTable n = nextTable的长度
Node<K,V> e; int n;
// 1、key = null 2、nextTable 为null 或者没有元素 3、key对应的索引位置上没有元素
// 这三种情况直接返回null
if (k == null || tab == null || (n = tab.length) == 0 ||
(e = tabAt(tab, (n - 1) & h)) == null)
return null;
for (;;) {
int eh; K ek;
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
// 找到,返回
return e;
if (eh < 0) {
// 如果 e 是 ForwardingNode 类型的,说明元素查找过程中table又一次扩容了,从新的表中去查找
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
// 跳到外部循环
continue outer;
}
else
// e 可能是 TreeBin节点,也有可能是 ReservationNode节点,使用相应的子类方法去查找
return e.find(h, k);
}
// e = e.next 往下遍历
if ((e = e.next) == null)
return null;
}
}
}
}
/**
* 在computeIfAbsent和compute中使用的位置保持节点
* A place-holder node used in computeIfAbsent and compute
*/
static final class ReservationNode<K,V> extends Node<K,V> {
ReservationNode() {
// RESERVED = -3
super(RESERVED, null, null, null);
}
// 直接返回 null
Node<K,V> find(int h, Object k) {
return null;
}
}
/* ---------------- Table Initialization and Resizing -------------- */
/**
* Returns the stamp bits for resizing a table of size n.
* 当通过RESIZE_STAMP_SHIFT左移时,必须为负。
* Must be negative when shifted left by RESIZE_STAMP_SHIFT.
*/
// n = table.length
static final int resizeStamp(int n) {
// numberOfLeadingZeros()返回最高位的1的左边的0的个数
// RESIZE_STAMP_BITS = 16
// 1 << (RESIZE_STAMP_BITS - 1) -> 1000 0000 0000 0000 0000
// -> | (1 << (RESIZE_STAMP_BITS - 1)) 的目的是,生成的值向左移动16位后的数一定是负数
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
/**
* 使用sizeCtl中记录的size初始化表。
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
// 把 sizeCtl 赋值先赋值给 sc,因为下面会先修改 sizeCtl的值
if ((sc = sizeCtl) < 0)
// 初始化竞争失败,仅仅自旋
Thread.yield(); // lost initialization race; just spin
// CAS设置 sizeCtl值为 -1,成功才能对 table进行初始化
// sizeCtl 使用volatile修饰
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
// 若两个线程同时进入了 while()块里面,第一个线程完成初始化并重新设置sizeCtl后,
// 第二个线程就有可能进入到这里,所以还需要判断 table是否已经初始化了
if ((tab = table) == null || tab.length == 0) {
// 若sc = 0 表示没有指定table初始化的size,因此将会使用默认的size, DEFAULT_CAPACITY = 16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
// 初始化 table
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
// sc = n - (1/4)n = 0.75 * n;
sc = n - (n >>> 2);
}
} finally {
// 修改sizeCtl的值,不需要使用CAS,因此此时相当于获取到了锁
sizeCtl = sc;
}
break;
}
}
return tab;
}
/**
* 添加到count,如果表太小且还没有调整大小,则启动transfer
* Adds to count, and if table is too small and not already
* resizing, initiates transfer. If already resizing, helps
* perform transfer if work is available.
* 在转移后重新检查占用情况,看看是否已经需要另一个大小调整,因为大小调整是滞后的添加。
* Rechecks occupancy
* after a transfer to see if another resize is already needed
* because resizings are lagging additions.
*
* @param x the count to add
* @param check if <0, don't check resize, if <= 1 only check if uncontended
*/
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
// 一旦 counterCells != null, 就不会再修改 baseCount的值
if ((as = counterCells) != null ||
// 使用CAS修改 baseCount的值,在原来的值上 +x. s = b + x
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
// --- counterCells != null || CAS更新 baseCount 失败 ----
CounterCell a; long v; int m;
boolean uncontended = true;
// ------ ConcurrentHashMap 并不是元素个数达到 sizeCtl就会进行扩容 -----------
// 1、as == null || (m = as.length - 1) < 0 说明 counterCells还没有初始化
// 2、(a = as[ThreadLocalRandom.getProbe() & m]) == null 该线程对应的counterCells上的元素为null
// 3、CAS更新 counterCell上的数据失败
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
// 计数值 + x
fullAddCount(x, uncontended);
// 以上所述的3种情况,完成计数相加之后,方法直接返回,不会进行扩容
return;
}
// CAS 修改相应的counterCell 上的值成功之后,判断check的值,小于等于1直接返回,不会进行扩容,也就是该 slot上的
// 元素个数,在插入之前至少应该有两个 (加上新插入的元素至少有3个) 才会去判断是否进行扩容
if (check <= 1)
return;
// 统计元素个数
s = sumCount();
}
// check >= 0 才会判断是否进行扩容, 删除元素时传入的是 -1,所以不用进行扩容判断
if (check >= 0) {
// nt -> nextTable, sc -> sizeCtl
Node<K,V>[] tab, nt; int n, sc;
// s = b + x, b = baseCount.
// 当元素个数达到 sizeCtl,并且 table长度小于MAXIMUM_CAPACITY,则进行扩容
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
// n -> tab.length. stamp可确保大小调整不重叠
int rs = resizeStamp(n);
if (sc < 0) {
// RESIZE_STAMP_SHIFT = 16
// 1、(sc >>> RESIZE_STAMP_SHIFT) != rs ==> sc 向右无符号移动16位后,低16位的值都被舍弃掉了,如果结果为true说明同时
// 进行transfer() 操作的线程已经达到了 65535个 ( 1 << 16的值为 65536,而第一个transfer()的线程把sc的值设置为
// ( rs << RESIZE_STAMP_SHIFT) + 2) 本来应该是加1的,多加了1,因此只要同时进行 transfer()的线程达到 65535个,
// 结果就会为true. 注意: rs的值是被移动到了高16位,因此sc低16位原来的值是0 );
// 2、sc == rs + 1 说明所有执行 transfer()的线程都已经完成了,这就说明扩容的元素转移已经完成,只剩下最后的检查和finishing操作
// 3、sc == rs + MAX_RESIZERS 说明进行 transfer()的线程数量已经达到最大了 (MAX_RESIZERS = 65535);
// 4、(nt = nextTable) == null 说明已经完成扩容了,完成的操作先修改 nextTable = null,然后再修改sizeCtl的值,因此 sc < 0 时,
// nextTable = null;
// 5、transferIndex <= 0 说明原来table上的所有索引位置已经全部分配完成了
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
// sc 计数+1
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
// sc >= 0 . 使用 CAS修改 sc的值为负数,成功才能执行 transfer() 方法
// 注意: (rs << RESIZE_STAMP_SHIFT) + 2 一定是个负数, sc 小于0 说明正在进行扩容
// todo: + 2 的作用? 如上第 1 点所述
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
/**
* 如果正在调整大小,则帮助转移。
* Helps transfer if a resize is in progress.
*/
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
// 根据 tab.length 计算 stamp 值
int rs = resizeStamp(tab.length);
// table 扩容时 sizeCtl < 0,否则说明已经完成扩容了
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
// 1、(sc >>> RESIZE_STAMP_SHIFT) != rs ==> sc 向右无符号移动16位后,低16位的值都被舍弃掉了,如果结果为true说明同时
// 进行transfer() 操作的线程已经达到了 65535个 ( 1 << 16的值为 65536,而第一个transfer()的线程把sc的值设置为
// ( rs << RESIZE_STAMP_SHIFT) + 2) 本来应该是加1的,多加了1,因此只要同时进行 transfer()的线程达到 65535个,
// 结果就会为true. 注意: rs的值是被移动到了高16位,因此sc低16位原来的值是0 );
// 2、sc == rs + 1 说明所有执行 transfer()的线程都已经完成了,这就说明扩容的元素转移已经完成,只剩下最后的检查和finishing操作
// 3、sc == rs + MAX_RESIZERS 说明进行 transfer()的线程数量已经达到最大了 (MAX_RESIZERS = 65535);
// 4、(nt = nextTable) == null 说明已经完成扩容了,完成的操作先修改 nextTable = null,然后再修改sizeCtl的值,因此 sc < 0 时,
// nextTable = null;
// 5、transferIndex <= 0 说明原来table上的所有索引位置已经全部分配完成了
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
// 使用CAS把 sc的值设置为 sc + 1,表示多了一个线程进行 transfer()操作
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
// 完成扩容,table就是新的表了
return table;
}
/**
* 尝试调整表的大小以容纳给定的元素数量。
* Tries to presize table to accommodate the given number of elements.
*
* @param size number of elements (doesn't need to be perfectly accurate)
*/
// size 不是table的大小,而是根据size计算出c的值,c为 table 能容纳的元素个数
// 若 size 为32,那么 c = 64,table需要扩容到 128
private final void tryPresize(int size) {
// MAXIMUM_CAPACITY = 1 << 30
// tableSizeFor(c) 返回大于等于c的最小的2的幂的数
int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
tableSizeFor(size + (size >>> 1) + 1);
int sc;
// 1、sizeCtl = -1 说明table 正在进行初始化;
// 2、sizeCtl 是负数,且不等于 -1,说明table正在进行扩容。
// 若 sc < 0,则什么都不干,方法结束
// 使用 while 循环的原因是: table 一次扩容只能长度只能增加一倍,而不能一次扩容到想要的长度,
// 因此只有满足 c <= sc || n >= MAXIMUM_CAPACITY 条件才能结束,或者出现并发,使 sizeCtl < 0
while ((sc = sizeCtl) >= 0) {
Node<K,V>[] tab = table; int n;
// 若 table 还没有初始化
if (tab == null || (n = tab.length) == 0) {
// 若 sc > c,则取sc的值,否则取c的值。因为 table初始化之前,sizeCtl保存的是
// table的初始容量
n = (sc > c) ? sc : c;
// 使用CAS修改sc的值为 -1
if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
// 获取到锁(CAS成功)后还需要判断table是否已经被修改了
if (table == tab) {
// 初始化table
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = nt;
// sc = 0.75 * n
sc = n - (n >>> 2);
}
} finally {
// 修改sizeCtl的值,不需要使用CAS,因此此时相当于获取到了锁
sizeCtl = sc;
}
}
}
// ------ table 已经初始化 -----
// 只有当 table 能容纳c个元素元素,获取达到了最大容量,才能退出
else if (c <= sc || n >= MAXIMUM_CAPACITY)
break;
// 判断 tab 是否等于table,不等于说明table已经扩容了,继续循环
else if (tab == table) {
int rs = resizeStamp(n);
if (sc < 0) {
Node<K,V>[] nt;
// RESIZE_STAMP_SHIFT = 16
// 1、(sc >>> RESIZE_STAMP_SHIFT) != rs ==> sc 向右无符号移动16位后,低16位的值都被舍弃掉了,如果结果为true说明同时
// 进行transfer() 操作的线程已经达到了 65535个 ( 1 << 16的值为 65536,而第一个transfer()的线程把sc的值设置为
// ( rs << RESIZE_STAMP_SHIFT) + 2) 本来应该是加1的,多加了1,因此只要同时进行 transfer()的线程达到 65535个,
// 结果就会为true. 注意: rs的值是被移动到了高16位,因此sc低16位原来的值是0 );
// 2、sc == rs + 1 说明所有执行 transfer()的线程都已经完成了,这就说明扩容的元素转移已经完成,只剩下最后的检查和finishing操作
// 3、sc == rs + MAX_RESIZERS 说明进行 transfer()的线程数量已经达到最大了 (MAX_RESIZERS = 65535);
// 4、(nt = nextTable) == null 说明已经完成扩容了,完成的操作先修改 nextTable = null,然后再修改sizeCtl的值,因此 sc < 0 时,
// nextTable = null;
// 5、transferIndex <= 0 说明原来table上的所有索引位置已经全部分配完成了
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
// sc 计数+1,成功帮忙 transfer()
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
// sc >= 0 . 使用 CAS修改 sc的值为负数,成功才能执行 transfer() 方法
// 注意: (rs << RESIZE_STAMP_SHIFT) + 2 一定是个负数, sc 小于0 说明正在进行扩容
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
}
}
}
/**
* 移动 和/或 复制每个bin里面的 nodes 到新的table
* Moves and/or copies the nodes in each bin to new table. See
* above for explanation.
*/
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
// (n >>> 3) / NCPU -> n / 8 / NCPU
// MIN_TRANSFER_STRIDE = 16, stride的值至少为 16
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range 细分范围
// 初始化 nextTab、 nextTable、transferIndex
if (nextTab == null) { // initiating
try {
// 新创建一个数组,大小为原来的两倍
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
// 初始化 nextTab
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
// OOME -> OutOfMemoryError 内存溢出错误
sizeCtl = Integer.MAX_VALUE;
return;
}
// 赋值 nextTable
nextTable = nextTab;
// n -> 旧table的长度
transferIndex = n;
}
int nextn = nextTab.length;
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
boolean advance = true;
// 确保在提交nextTab之前进行清理
boolean finishing = false; // to ensure sweep before committing nextTab
// for循环遍历
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
// 线程一旦执行了transfer()方法,则必须等所有的索引位置都分配完成才能退出
while (advance) {
int nextIndex, nextBound;
// --i 先减1再比较, 即每循环一次, i 都会先减 1
// 如果 --i >= bound,那么只需要把 advance赋值为 false,然后继续往下遍历
if (--i >= bound || finishing)
advance = false;
// transferIndex -> 创建nextTable时,会赋值为旧table的长度
// (nextIndex = transferIndex) <= 0 说明原来table上的所有索引位置已经全部分配完成了
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
// nextIndex = transferIndex, 使用CAS修改 transferIndex的值
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
// 从旧table的最后一个索引位置往前遍历,最后一个索引为 transferIndex - 1
i = nextIndex - 1;
advance = false;
}
}
// n -> 旧table的长度, nextn -> 新table的长度 ( 2n )
// i < 0 || i >= n 不在旧 table的索引范围内, i + n >= nextn ===> i + n >= 2n ===> i >= n
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
if (finishing) {
// 扩容完成, nextTable 置为 null
nextTable = null;
// 把 nextTab 赋值给 table
table = nextTab;
// 重新赋值 sizeCtl, 大小为新的 table长度 * 0.75
sizeCtl = (n << 1) - (n >>> 1);
return;
}
/**
第一个扩容的线程,执行transfer方法之前,会设置 sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2)
后续帮其扩容的线程,执行transfer方法之前,会设置 sizeCtl = sizeCtl+1
每一个退出transfer的方法的线程,退出之前,会设置 sizeCtl = sizeCtl-1
那么最后一个线程退出时:
必然有sc == (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2),即 (sc - 2) == resizeStamp(n) << RESIZE_STAMP_SHIFT
*/
// 不相等,说明不到最后一个线程,直接退出transfer方法
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
// 最后一个完成移动的线程遍历整个table,等所有索引位置的元素都移动完成之后,才能执行 finishing 操作
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
// 只有最后一个完成transfer任务的线程能够执行到这里,其他线程都直接返回了
finishing = advance = true;
// 所有的索引位置都要检查一遍
i = n; // recheck before commit
}
}
// 若索引i没有元素,则设置为 ForwardingNode
else if ((f = tabAt(tab, i)) == null)
// CAS成功则 advance = true,继续处理下一个slot,失败则继续
advance = casTabAt(tab, i, null, fwd);
else if ((fh = f.hash) == MOVED)
// 该索引位置已经完成了元素移动
advance = true; // already processed 已处理
else {
// 首先需要先拿到索引第一个元素的锁(监视器)
synchronized (f) {
// 拿到锁后需要判断该索引的第一个元素是否还是f,如果不是,继续循环
if (tabAt(tab, i) == f) {
// ln -> 低索引位置的第一个元素 hn -> 高索引位置的第一个元素
// 比如原来的长度为16,若索引位置3的元素如果第5位(1 0000)是1那么分配到
// 高索引位置19上,否则还是在索引位置3上
Node<K,V> ln, hn;
if (fh >= 0) {
int runBit = fh & n;
Node<K,V> lastRun = f;
// todo: 为什么要把最后一个元素先取出来
// --> 因为:1、最后一个节点的next字段肯定不会改变,一定是 null,可以重用这个节点
// 2、还有一个作用就是,如果线程使用原来的table进行遍历,那么在遍历之前
// 新插入的节点还能被看到
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
// 比如长度为 16 (1 0000), 那么最后一个元素hash值的第五位如果是0,那么就赋值给 ln,
// 第五位如果是1,那么就赋值给 hn
if (runBit == 0) {
ln = lastRun;
// hn 还没初始化,所以初始化为 null
hn = null;
}
else {
hn = lastRun;
// ln 还没初始化,所以初始化为 null
ln = null;
}
// 从第一个元素开始遍历,直到最后一个元素(不再包括最后一个元素了)
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
// 重新创建节点的原因的: 其他线程可能还在进行遍历,因此不能修改原来节点的 next字段,只能重新创建
// 重新创建Node,并传入了 next
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
// 把ln、 hn 赋值到新table相应的位置上
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
// 原来table的 i索引位置上的元素赋值为 fwd (ForwardingNode)
setTabAt(tab, i, fwd);
// 设置 advance = true,继续往下遍历
advance = true;
}
else if (f instanceof TreeBin) {
TreeBin<K,V> t = (TreeBin<K,V>)f;
// lo -> 低位的第一个元素,loTail -> 低位的最后一个元素
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
// 使用链表进行遍历
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
// 创建新的节点,保持原来的结构不变
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
// 设置 p.prev = loTail
if ((p.prev = loTail) == null)
lo = p;
else
// loTail不为null,设置loTail.next = p
loTail.next = p;
// 低位最后一个元素设置为p
loTail = p;
++lc; // 低位元素个数 + 1
}
else {
// 设置 p.prev = loTail
if ((p.prev = hiTail) == null)
hi = p;
else
// hiTail不为null,设置hiTail.next = p
hiTail.next = p;
// 高位最后一个元素设置为p
hiTail = p;
++hc; // 高位元素个数 + 1
}
}
// UNTREEIFY_THRESHOLD = 6, 如果扩容后元素个数小于等于6,那么把红黑树转成链表
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
// 如果高位的元素个数为0,说明扩容后元素全部分配到低位上,直接使用原来的TreeBin
// 否则重新构造新的TreeBin
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
// 设置低位元素
setTabAt(nextTab, i, ln);
// 设置高位元素
setTabAt(nextTab, i + n, hn);
// 扩容后把该索引位置上的元素设置为:ForwardingNode
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
/* ---------------- Counter support -------------- */
/**
* 用于分配计数的填充单元格.改编自LongAdder and Striped64
* A padded cell for distributing counts. Adapted from LongAdder
* and Striped64. See their internal docs for explanation.
*/
@sun.misc.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
// 统计元素个数, 并发更新下不保证是准确的值
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
// 统计 baseCount 和 counterCells中的每个元素总和
long sum = baseCount;
if (as != null) {
// 遍历 counterCells
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
// See LongAdder version for explanation
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
// ThreadLocalRandom.getProbe() 获取当前线程的probe值,如果probe值为0,那么则初始化线程的probe值
if ((h = ThreadLocalRandom.getProbe()) == 0) {
// 初始化当前线程的probe值
ThreadLocalRandom.localInit(); // force initialization
h = ThreadLocalRandom.getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
// 判断 counterCells 不为null, 且长度大于 0
if ((as = counterCells) != null && (n = as.length) > 0) {
// 如果当前线程对应的索引位置的元素为 null
if ((a = as[(n - 1) & h]) == null) {
// 判断锁是否可以获取
if (cellsBusy == 0) { // Try to attach new Cell
// 乐观地创建(乐观情况下可以获取到锁,因此先创建好 CounterCell)
CounterCell r = new CounterCell(x); // Optimistic create
// 使用CAS把 cellsBusy 的值从 0修改为1, 从而获取锁
if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
// 获取到锁后需要再次判断,重新获取counterCells是因为: counterCells可能已经扩容了,不是原来那个数组了,
// 再次判断该索引位置的元素是否为空的原因是:其他线程可能已经更新的该索引位置的值,有可能不为null了
if ((rs = counterCells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
// 不需要使用UNSAFE进行更新,因为 finally{} 会更新 cellsBusy的值,而cellsBusy是volatile修饰的,
// 修改cellsBusy之前所有的缓存数据都会被刷新到主存
rs[j] = r;
created = true;
}
} finally {
// 释放锁,同时由于 cellsBusy是volatile修饰的,因此可以把缓存中的数据都刷新到主存,保证对
// counterCells的修改立即对其他线程可见
cellsBusy = 0;
}
if (created)
// 把第一个值放入CounterCell后就相当于完成了加法操作,因此直接break
break;
// 该索引位置现在不为null了,continue
continue; // Slot is now non-empty
}
}
// collide 只有在相应的slot不为null且 CAS失败才会为true,此时检查到
// slot 为null,说明 countCells扩容了,因此修改 collide = false
collide = false;
}
// -------- 该索引位置上已经有元素了 ---------
// 如果 wasUncontended = false, 说明调用这个方法之前该索引位置已经使用CAS更新过一次
// 并且失败了
else if (!wasUncontended) // CAS already known to fail
// 下面会修改线程 probe的值,相当于修改了该线程对应countCells的索引,然后进行重试
wasUncontended = true; // Continue after rehash
// *** 该索引位置上的CounterCell不为空,使用CAS更新数据,就算countCells已经扩容了,那么也没有关系,
// 因为新的 countCells 数据上的元素指向的还是旧countCells上的元素,都是指向相同的对象,
// 在旧countCells上更新数据也是一样的
// * 如果CAS失败了,那么下面的else if 还会继续判断
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
// counterCells 已经达到了最大的长度 或者 counterCells != as(counterCells扩容了)
else if (counterCells != as || n >= NCPU)
// 设置 collide = false, 因为此时 collide 可能为 true
// 然后下面会修改线程probe的值
collide = false; // At max size or stale
else if (!collide)
// 第一次冲突只设置 collide = true, 然后下面会修改线程 probe的值,
// 如果下次还是再冲突,那么就会对 countCells进行扩容
collide = true;
// 连续两次冲突,获取锁
else if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
// 判断其他线程是否已经对counterCells进行了扩容
if (counterCells == as) {// Expand table unless stale
// 长度扩大一倍
CounterCell[] rs = new CounterCell[n << 1];
for (int i = 0; i < n; ++i)
// 把旧 counterCells中的元素都赋值到新的 counterCells中
rs[i] = as[i];
counterCells = rs;
}
} finally {
// 释放锁,同时由于 cellsBusy是volatile修饰的,因此可以把缓存中的数据都刷新到主存,保证对
// counterCells的修改立即对其他线程可见
cellsBusy = 0;
}
// 修改collide = false
collide = false;
continue; // Retry with expanded table
}
h = ThreadLocalRandom.advanceProbe(h);
}
// ------- counterCells 还没有初始化 -------------------
// 获取锁
else if (cellsBusy == 0 && counterCells == as &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // Initialize table
// 判断 counterCells是否已经被其他线程初始化了
if (counterCells == as) {
// 初始化counterCells, 初始容量为 2
CounterCell[] rs = new CounterCell[2];
// 创建CounterCell,value为要相加的值x, 并把counterCell放入 counterCells 相应的索引位置
rs[h & 1] = new CounterCell(x);
counterCells = rs;
init = true;
}
} finally {
// 释放锁,同时由于 cellsBusy是volatile修饰的,因此可以把缓存中的数据都刷新到主存,保证对
// counterCells的修改立即对其他线程可见
cellsBusy = 0;
}
if (init)
// 因为要相加的值已经放入了 CounterCell中,等价于完成了相加,因此方法结束
break;
}
// countCells 已经被其他线程初始化,或者其他线程已经拿到了 cellsBusy锁
// 更新baseCount 的值
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
// 回到使用 base
break; // Fall back on using base
}
}
/* ---------------- Conversion from/to TreeBins -------------- */
/**
* 把容器中给定索引位置的链表节点替换成树节点,除非table太小,这种情况下将对table进行扩容
* Replaces all linked nodes in bin at given index unless table is
* too small, in which case resizes instead.
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
// MIN_TREEIFY_CAPACITY = 64,如果table长度没有达到 64,那么不会把链表转成红黑树,只会对table进行扩容
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
// 如果table的长度为 16,即 n = 16, (n << 1) 的值为32,tryPresize(32)会把table的长度扩大到128
tryPresize(n << 1);
// b.hash >= 0 说明 b节点属于 链表节点
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
// 获取锁,使用链表的第一个节点作为锁
synchronized (b) {
/**
* 转红黑树过程中不判断table是否正在进行扩容是因为:如果在不使用sizeCtl加锁的情况下,判断table没有在
* 进行扩容,在转红黑树的过程中还是有可能进行扩容,因此没有什么用。如果进行加锁,那么转红黑树的
* 过程中就没办法进行扩容,效率太低。因此不判断。
*/
// 获取到锁后判断第一个节点是否还是原来的节点,如果已经被其他线程转成树节点了,那么不会相等
if (tabAt(tab, index) == b) {
// hd表示第一个节点, tl 表示上一个节点
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
// 创建 TreeNode,next 、parent 传入 null
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
// 修改 p 的 prev 节点
if ((p.prev = tl) == null)
// 如果 tl = null,那么 p 就是第一个节点,所以 hd = p
hd = p;
else
// 上一个节点的 next = p
tl.next = p;
// tl = p
tl = p;
}
// 把红黑树封装到 TreeBin中,构造方法中会把链表转成红黑树
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
/**
* Returns a list on non-TreeNodes replacing those in given list.
*/
static <K,V> Node<K,V> untreeify(Node<K,V> b) {
// hd -> head; tl 上一个节点
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = b; q != null; q = q.next) {
Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
if (tl == null)
// tl = null ,说明p 是第一个节点
hd = p;
else
// 修改上一个节点的next
tl.next = p;
tl = p;
}
return hd;
}
/* ---------------- TreeNodes -------------- */
/**
* Nodes for use in TreeBins
*/
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
// 删除节点的时候需要修改上一个的节点的 next 值
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next,
TreeNode<K,V> parent) {
super(hash, key, val, next);
this.parent = parent;
}
Node<K,V> find(int h, Object k) {
return findTreeNode(h, k, null);
}
/**
* 从调用这个方法的节点开始查找,没有找到返回null
* Returns the TreeNode (or null if not found) for the given key
* starting at given root.
*/
final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
if (k != null) {
// 从调用这个方法的 TreeNode开始查找
TreeNode<K,V> p = this;
do {
int ph, dir; K pk; TreeNode<K,V> q;
TreeNode<K,V> pl = p.left, pr = p.right;
if ((ph = p.hash) > h)
// ph > h 从左子树查找
p = pl;
else if (ph < h)
// ph < h 从右子树查找
p = pr;
// ----- hash值相等 ------
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
// p.key 和 key 比较,只有当 p.key == key || k.equals(pk)时才是相等
// 找到了,返回
return p;
else if (pl == null)
// p左儿子儿子为空,所以只能从右儿子开始找
p = pr;
else if (pr == null)
// p右儿子为空,所以只能从左儿子开始找
p = pl;
// 走到这里说明哈希值相等 且 p.key != k 且 k.equals(pk) == false 且 左右子树都不为空
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
// comparable 比较结果不相等
p = (dir < 0) ? pl : pr;
// 哈希值相等 且 ( kc == null 或者comparable比较结果相等),这种情况下没有办法判断是从左子树
// 去查找还是从右子树去查找,所以左右子树都要查找
// 首先从右子树去查找,没有找到再从左子树去查找
// 这里先从左子树去查找可能会好一点,因为比较结果相等的元素都插入在左子树,旋转后才会到右子树
/**
* P
* / \
* L R
* / \ / \
* L1 L2 R1 R2
* 比如 p.key 和 key 不相等,然后又无法知道从P的左节点还是右节点去查找,那么先从R
* 节点开始往下查找,如果没找到再从 L节点去查找
*/
else if ((q = pr.findTreeNode(h, k, kc)) != null)
// q != null 说明找到了,返回
return q;
else
// 从右子树中没有找到,继续从左子树中查找
p = pl;
// 当 p = null的时候结束查找
} while (p != null);
}
// 如果没有找到 p.key == key || k.equals(pk) 的元素,那么返回null
return null;
}
}
/* ---------------- TreeBins -------------- */
/**
* TreeBin 不保存用户的keys 和 values,而是指向treenode列表及其根。
* TreeNodes used at the heads of bins. TreeBins do not hold user
* keys or values, but instead point to list of TreeNodes and
* their root.
* 它们还维护一个寄生的读写锁,迫使写者(持有bin锁的人)在树重组操作之前等待读者(不持有的人)完成。
* They also maintain a parasitic read-write lock
* forcing writers (who hold bin lock) to wait for readers (who do
* not) to complete before tree restructuring operations.
*/
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;
// 链表的第一个节点,不一定是根节点
volatile TreeNode<K,V> first;
volatile Thread waiter;
// 锁
volatile int lockState;
// values for lockState
// 表示有线程获取到了写锁
static final int WRITER = 1; // set while holding write lock
// 设置,表示有线程正在等待获取写锁
static final int WAITER = 2; // set when waiting for write lock
// 读锁自增值,每一个线程获取读锁,lockState + 4
static final int READER = 4; // increment value for setting read lock
/**
* Tie-breaking utility for ordering insertions when equal
* hashCodes and non-comparable. We don't require a total
* order, just a consistent insertion rule to maintain
* equivalence across rebalancings. Tie-breaking further than
* necessary simplifies testing a bit.
*/
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
// 比较类名
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0)
// 如果类名比较结果相同,使用系统生成的哈希值进行判断
d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1);
return d;
}
/**
* Creates bin with initial set of nodes headed by b.
*/
TreeBin(TreeNode<K,V> b) {
super(TREEBIN, null, null, null);
// 把 b 赋值给 first
this.first = b;
// r 代表根节点
TreeNode<K,V> r = null;
for (TreeNode<K,V> x = b, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
// 初始化left、 right 字段
x.left = x.right = null;
if (r == null) {
// 若根节点为null, 那么 x 设置为根节点,根节点颜色为黑色,所以 red = false
x.parent = null;
x.red = false;
r = x;
}
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
for (TreeNode<K,V> p = r;;) {
// dir: direction -> 插入左节点还是右节点
// ph -> p的哈希值
// pk -> p的键
int dir, ph;
K pk = p.key;
// p 的哈希值大于插入key 的哈希值
if ((ph = p.hash) > h)
// -1 -> 在左子树
dir = -1;
else if (ph < h)
// 1 -> 在右子树
dir = 1;
// 先使用哈希值判断,哈希值相等再使用Comparable判断(如果插入的键类型是class C implements Comparable<C> )
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
// key不是class C implements Comparable<C>类型,或者 comparable比较结果相等的话,再使用类名判断,
// 如果类名还是相同,再使用系统生成的哈希值进行判断。最后的结果还是可能是相等的,即执行完成之后dir可能还是0
dir = tieBreakOrder(k, pk);
TreeNode<K,V> xp = p;
// 根节点开始,当 (p = (dir <= 0) ? p.left : p.right) == null的时候,xp就是插入元素x的父节点
// 比较结果相等的时候插入到左节点
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
// balanceInsertion()平衡红黑树,并返回新的根节点
r = balanceInsertion(r, x);
break;
}
}
}
}
this.root = r;
// 链表、红黑树结构检查
assert checkInvariants(root);
}
/**
* 获取用于树结构重组的写锁。
* Acquires write lock for tree restructuring.
*/
private final void lockRoot() {
// 获取写锁,修改 TreeBin的 lockState值,lockState的值为0才能获取到锁,也就是没有其他线程获取到读锁/写锁
if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
// 竞争锁
contendedLock(); // offload to separate method
}
/**
* 释放写锁
* Releases write lock for tree restructuring.
*/
private final void unlockRoot() {
// 释放写锁,把 lockState的值修改成 0
lockState = 0;
}
/**
* 可能阻塞等待 root lock
* Possibly blocks awaiting root lock.
*/
private final void contendedLock() {
boolean waiting = false;
// 如果有线程已经获取到了读锁(线程走到这里,不会有其他线程获取到写锁),并且lockState的第二位为1,
// 那么线程会一直自旋
for (int s;;) {
// WAITER = 2
// ~WAITER -> 1111 1111 1111 1101(低16位)
// ((s = lockState) & ~WAITER) == 0 ==> lockState 等于 0 或者 2 表示没有线程获取到读锁
if (((s = lockState) & ~WAITER) == 0) {
// lockState = 2 的时候表示当前线程正在等待获取写锁(不可能是其他线程,想要插入或删除元素需要先拿到第一个元素的锁)
// 使用CAS把 lockState 的值修改为1,尝试获取写锁, (当 lockState = 0 或者 2 的时候可以获取写锁)
if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
// 如果 waiting = true,把 waiter设置为null
if (waiting)
// 因为插入数据需要先获取到第一个节点的锁,因此不会同时有两个线程进入这里,所以waiter不可能是
// 其他线程,从而可以直接把 waiter设置为null。
waiter = null;
return;
}
}
// WAITER = 2 -> 0000 0000 0000 0010 (低16位)
else if ((s & WAITER) == 0) {
// lockState != 2
// 使用 CAS把 lockState 的第二位设置为1,其他位不变。表明有线程在等待写锁
if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
// waiting 设置为 true, waiter设置为当前线程
waiting = true;
// 因为已经设置了lockState的第二位为1,如果waiter不为null,那么最后一个释放读锁的线程
// 在释放读锁后会唤醒waiter线程。 如果线程在设置waiter之前,最后一个持有读锁的线程释放了
// 读锁,那么也没有关系,因为设置完waiter后还会再尝试获取一次锁,而这次一定能够获取到锁
waiter = Thread.currentThread();
}
}
// 如果有线程持有读锁,并且lockState的第二位已经设置成1了,且waiting = true,那么阻塞当前线程
else if (waiting)
// 阻塞当前线程
LockSupport.park(this);
}
}
/**
* 返回匹配的节点,如果没有则返回null。尝试从根目录使用树比较进行搜索,
* 但在锁定不可用时继续使用线性搜索。
* Returns matching node or null if none. Tries to search
* using tree comparisons from root, but continues linear
* search when lock not available.
*/
final Node<K,V> find(int h, Object k) {
if (k != null) {
for (Node<K,V> e = first; e != null; ) {
int s; K ek;
// WAITER = 2 -> 0000 0000 0000 0010(低16位)
// WRITER = 1 -> 0000 0000 0000 0001(低16位)
// WAITER|WRITER -> 0000 0000 0000 0011(低16位)
// ((s = lockState) & (WAITER|WRITER)) != 0 说明有线程已经获取到了写锁,或者等待获取写锁,
// 因此线程不能获取读锁,使用链表方式进行查找
if (((s = lockState) & (WAITER|WRITER)) != 0) {
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
// 找到,返回
return e;
// 没有找到遍历下一个节点
e = e.next;
}
// 一旦发现线程释放了写锁,并且没有线程等待获取写锁,那么将尝试获取读锁,使用红黑树查找的方法,
// 从根节点开始查找 (不能从正在遍历的节点开始查找,因为链表的顺序和红黑树的顺序完全没有联系)
// READER = 4 一个线程获取到读锁,lockState的值 + 4
else if (U.compareAndSwapInt(this, LOCKSTATE, s,
s + READER)) {
// 进入这里,说明已经成功获取到了读锁
TreeNode<K,V> r, p;
try {
// 如果根节点为 null,那么 p = null,方法返回p; 否则从根节点开始查找
p = ((r = root) == null ? null :
r.findTreeNode(h, k, null));
} finally {
Thread w;
// 释放锁,U.getAndAddInt() 循环使用CAS设置值,直到成功为止,并返回修改之前的值
// WAITER = 2 -> 0000 0000 0000 0010(低16位)
// READER = 4 -> 0000 0000 0000 0100(低16位)
// 修改之前lockSate的值为:0000 0000 0000 0110(低16位) 表示该线程是最后一个持有
// 读锁的线程,并且有线程正在等待获取写锁, 并且此时 waiter 不为null,那么唤醒等待获取
// 写锁的线程
if (U.getAndAddInt(this, LOCKSTATE, -READER) ==
(READER|WAITER) && (w = waiter) != null)
// 唤醒线程
LockSupport.unpark(w);
}
return p;
}
}
}
return null;
}
/**
* Finds or adds a node.
* null 表示添加了新的节点,否则返回 key映射的节点
* @return null if added
*/
// h -> 哈希值
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
Class<?> kc = null;
// searched 表示是否已经在整个红黑树中查找过 key是否存在了
boolean searched = false;
for (TreeNode<K,V> p = root;;) {
// dir: direction -> 插入左节点还是右节点
// ph -> p的哈希值
// pk -> p的键
int dir, ph; K pk;
// 如果树中没有任何节点,那么创建新的节点,并设置和 first 和 root
if (p == null) {
first = root = new TreeNode<K,V>(h, k, v, null, null);
break;
}
// p 的哈希值大于插入key 的哈希值
else if ((ph = p.hash) > h)
// -1 -> 在左子树
dir = -1;
else if (ph < h)
// 1 -> 在右子树
dir = 1;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
// p.key == key 或者 equals()方法返回true才是真正的相等,comparable只是比较一下存放的位置
// key 已存在, 返回已存在的节点,putVal()方法会判断是否更新原来的值
return p;
// 先判断哈希值,哈希值相等且equals()方法返回false时才会使用 comparable判断
else if ((kc == null &&
// comparableClassFor(k) 判断k 是否是 class C implements Comparable<C>
(kc = comparableClassFor(k)) == null) ||
// k -> 要插入的元素的key, pk -> p的 key
// 使用comparable 比较,若比较结果相等也相等(不能判断插入的元素是在左边还是在右边)
(dir = compareComparables(kc, k, pk)) == 0) {
// searched 初始值为false
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
// p这个元素在上面已经判断过不相等了,所以只要从左儿子开始找,左儿子没有找到再从右儿子去找
// -- 为什么从左儿子开始找,而不是右儿子? 因为哈希值dir = 0的时候,元素是存到左子树,只有旋转了
// 元素才可能跑到右子树,所以先从左儿子去找,找到元素的可能性比较大。
if (((ch = p.left) != null &&
(q = ch.findTreeNode(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.findTreeNode(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
// 通过上面的查找确定key在树中不存在,才能进行插入
TreeNode<K,V> xp = p;
// 哈希值相等,equals方法返回false的时候 dir = 0,等于0插入到左子树
// dir = 0的情况:哈希值相等,equals方法返回false,且comparable比较结果为0,且key在红黑树中不存在
// p == null 的时候, xp就是要就是插入节点的父节点
if ((p = (dir <= 0) ? p.left : p.right) == null) {
TreeNode<K,V> x, f = first;
// first 指向新的节点,新节点的next指向原来的first (这个和HashMap不一样)
// 构造方法传入 f ,设置了新 first的 next 节点,避免链表在一瞬间内断开连接
first = x = new TreeNode<K,V>(h, k, v, f, xp);
// 修改原来 first 的prev节点
if (f != null)
f.prev = x;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
if (!xp.red)
// 如果父节点是黑色的,那么设置当前节点为红色,插入完成,不需要进行旋转
x.red = true;
else {
// 红黑树旋转之前需要获取写锁,因为旋转会导致红黑树的结构会发生变化,
// 如果有线程正在遍历这个红黑树,遍历结果将不可控
lockRoot();
try {
// 红黑树平衡
root = balanceInsertion(root, x);
} finally {
// todo : 为什么释放写锁不用考虑唤醒其他线程?
// ---> 因为插入数据的时候需要先拿到第一个节点的锁,因此不存在两个线程同时修改
// 同一个索引上数据,从而线程只会等待读锁释放,而不会存在等待写锁释放的情况,所以释放
// 写锁的时候不用考虑唤醒其他线程
// 释放写锁。
unlockRoot();
}
}
break;
}
}
assert checkInvariants(root);
return null;
}
/**
* 删除给定的节点,在调用这个方法之前,必须先确定删除节点存在
* Removes the given node, that must be present before this
* 这比典型的红黑删除代码更混乱,因为我们不能使用右子树的最小节点来交换内部节点的内容,
* 因为"next"固定了节点的顺序,而“next”指针是可以独立于lock访问的。
* call. This is messier than typical red-black deletion code
* because we cannot swap the contents of an interior node
* with a leaf successor that is pinned by "next" pointers
* that are accessible independently of lock. So instead we
* 所以我们交换树状连杆。
* swap the tree linkages.
*
* 如果删除后红黑树的结构太小,那么返回true,表示需要把红黑树转成链表
* @return true if now too small, so should be untreeified
*/
final boolean removeTreeNode(TreeNode<K,V> p) {
TreeNode<K,V> next = (TreeNode<K,V>)p.next;
TreeNode<K,V> pred = p.prev; // unlink traversal pointers
TreeNode<K,V> r, rl;
if (pred == null)
// pred = null,说明删除的节点是 第一个节点
first = next;
else
// pred.next = next,即把要删除的节点从链表中删除
pred.next = next;
// 如果next 不为null,修改 next的prev节点
if (next != null)
next.prev = pred;
if (first == null) {
root = null;
// 该索引位置没有任何节点了,返回true,把红黑树节点转成链表节点
return true;
}
// 红黑树的结构至少要是以下这样的,否则返回true,把红黑树转成链表
// root
// / \
// rl rr
// /
// rll
// r = root
if ((r = root) == null || r.right == null || // too small
(rl = r.left) == null || rl.left == null)
return true;
// 获取写锁,对红黑树的结构更新,需要获取到写锁
lockRoot();
try {
TreeNode<K,V> replacement;
TreeNode<K,V> pl = p.left;
TreeNode<K,V> pr = p.right;
// 删除节点的左右子树都不为null
if (pl != null && pr != null) {
TreeNode<K,V> s = pr, sl;
// p
// / \
// pl s(pr)
// /
// sl
// 1、查找右子树中最小的元素,比如上图中的s1
while ((sl = s.left) != null) // find successor
s = sl;
// 现在 s 是删除元素右子树中最小的元素
// 2、 s 和 p 互换颜色和位置,然后再去删除p
// 2.1 互换颜色
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
// 右子树中最小的元素左节点一定为空,所以只要先把s的右节点保存下来就行了
TreeNode<K,V> sr = s.right;
TreeNode<K,V> pp = p.parent;
// 2.2 把S 和 P互换位置
// 判断s(右子树最小的元素) 是否是 p的右节点,这个判断很重要,否则就会出现 p.parent = p
if (s == pr) { // p was s's direct parent
// s 的右子树已经赋值给 sr了,下面会把sr 添加到 p的右子树
p.parent = s; // p.right,p.left 还没修改,下面会去修改,和else情况是公共的逻辑
s.right = p; // s.left,s.parent 还没修改,下面会去修改,和else情况是公共的逻辑
}
// s不是p的右节点
else {
TreeNode<K,V> sp = s.parent;
if ((p.parent = sp) != null) {
if (s == sp.left) // s 在 sp的左子树
sp.left = p; // sp的左子树变成 p
else
sp.right = p; // sp的右子树变成 p
}
// s.right = pr
if ((s.right = pr) != null)
pr.parent = s;
}
// p 和 s互换位置,s是p右子树的最小元素,所以s的左子树一定为空,换位置后 p.left = null
p.left = null;
// p.right = sr
if ((p.right = sr) != null)
sr.parent = p;
// s.left = pl
if ((s.left = pl) != null)
pl.parent = s;
// s.parent = pp
if ((s.parent = pp) == null)
// pp == null,说明p是根节点,换位置后s变成根节点,这里只是把s赋值给了r,并没有赋值给root
r = s;
// 修改pp的子节点
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
// 判断sr是否为null,sr不为null,那么删除p节点只要把sr移动到p的位置上,颜色变成 p节点的颜色即可
if (sr != null)
replacement = sr;
else
replacement = p;
}
// pl、pr 有一个为null,或者全部为null
else if (pl != null)
// 把pl移动到p的位置上,颜色变成p的颜色即可 (这种情况pl的颜色一定是红色,所以p的颜色一定是黑色)
replacement = pl;
else if (pr != null)
// 把 pr移动到p的位置上,颜色变成p的颜色即可
replacement = pr;
else
// 这种情况如果p节点的颜色不是红色的,那么需要对红黑树进行平衡处理
replacement = p;
// replacement != p ,那么只需要把 replacement移动到 p位置上,把replacement的颜色变成p的颜色即可
// (replacement != p的情况:1、删除节点有且只有一个节点不为null;2、删除节点移动后,右子节点不为null)
if (replacement != p) {
// 使用replacement节点替换p节点,然后修改颜色是在balanceDeletion() 方法里面
TreeNode<K,V> pp = replacement.parent = p.parent;
// pp为null,说明p是根节点,然后p的左右子树有一个为null时,有replacement != p,
// 但是对红黑树的结构限制后,根节点的左右子节点都不能为null, 因此这种情况不存在
// 如果p的左右子树都不为null,那么互换位置后p的父节点pp不可能为null
// 因此: replacement != p 时 pp = null不存在
if (pp == null)
// pp = null说明删除的 p节点是根节点,那么现在 replacement 变成了根节点
// 这里只是把 replacement 赋值给了局部变量r,并没有赋值给 root。
r = replacement;
// 修改父节点的子节点
else if (p == pp.left)
pp.left = replacement;
else
pp.right = replacement;
// p的左右节点和父亲都设置为null (树节点的遍历需要拿到锁,所以可以进行clear)
p.left = p.right = p.parent = null;
}
// p的左右节点至少有一个为空,如果p 的颜色是红色的,那么p的左右节点肯定都为空(因为不能连续两个红色的节点)
// 所以如果p 的颜色是红色的,那么直接删除即可
// 如果replacement != p那么replacement 一定是红色的,而p一定是黑色的,balanceDeletion()方法会把
// replacement的颜色变成黑色;如果replacement = p,那么p的颜色可能是红色,也可能是黑色的
root = (p.red) ? r : balanceDeletion(r, replacement);
// replacement != p在上面已经把p节点删除了,而 replacement = p还没把p节点删除,
// 在这里把 p节点删除
if (p == replacement) { // detach pointers
TreeNode<K,V> pp;
// 删除p节点,p == replacement,那么p的左右子节点一定都为null,如果此时p.parent = null,
// 那么该索引位置就只剩下p节点了,程序不会走到这里来
if ((pp = p.parent) != null) {
if (p == pp.left)
pp.left = null;
else if (p == pp.right)
pp.right = null;
// p == replacement,那么p的左右子节点一定都为null.所以只需要设置parent = null即可
p.parent = null;
}
}
} finally {
// 释放锁
unlockRoot();
}
assert checkInvariants(root);
// 返回false,不需要把红黑树转成链表
return false;
}
/* ------------------------------------------------------------ */
// Red-black tree methods, all adapted from CLR
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null) // p.right = r.left
// 若 r.left != null,修改 r.left.parent = p;
rl.parent = p;
if ((pp = r.parent = p.parent) == null) // r.parent = p.parent
// 如果p.parent == null,那么p就是原来的根节点,旋转后r成为新的根节点
// root = r, 且设置颜色为黑色
(root = r).red = false;
else if (pp.left == p)
// 如果p是pp 的左儿子,修改pp的左儿子为r
pp.left = r;
else
// 如果p是pp 的右儿子,修改pp的右儿子为r
pp.right = r;
// 修改r.left = p 和 p.parent = r
r.left = p;
p.parent = r;
}
// 返回新的根节点(红黑色的根节点而不是子树的根节点)
return root;
}
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> l, pp, lr;
if (p != null && (l = p.left) != null) { // l = p.left
if ((lr = p.left = l.right) != null) // p.left = r.right
// 如果 r.right != null,修改r.right.parent = p
lr.parent = p;
if ((pp = l.parent = p.parent) == null) // l.parent = p.parent
// 如果p.parent == null,那么p就是原来的根节点,旋转后r成为新的根节点
// 并设置颜色为黑色
(root = l).red = false;
// 修改pp的儿子
else if (pp.right == p)
pp.right = l;
else
pp.left = l;
l.right = p;
p.parent = l;
}
// 返回新的根节点(红黑色的根节点而不是子树的根节点)
return root;
}
// 方法返回根节点
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
// 新插入节点的颜色为红色
x.red = true;
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
if ((xp = x.parent) == null) {
// 如果x 是根节点,把x 的颜色修改成黑色
x.red = false;
// 返回的情况只有两种:
// 1、x是根节点的时候返回
return x;
}
else if (!xp.red || (xpp = xp.parent) == null)
// 2、 xp是黑色的了,说明没有两个连续红色的节点了,返回
// 如果xp是黑色的 或者 xp.parent == null (说明xp就是根节点,xp是根节点,xp不可能是红色的)
return root;
if (xp == (xppl = xpp.left)) {
// xp 是红色的,且xp 是xpp 的左子树
if ((xppr = xpp.right) != null && xppr.red) {
// xp的兄弟节点是红色的,这种情况不能通过旋转达到平衡,得上溯,
// 把 xppl(xp) 和xppr变成黑色,然后xp变成红色的 (xp是红色的,xpp肯定是黑色的)
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
// xp的兄弟节点是黑色的,可以通过旋转达到平衡,但是需要判断单旋转还是双旋转
if (x == xp.right) {
// 之字形,需要左右旋转。先进行左旋处理
// xpp(b) xpp(b)
// / \ 旋转之前先把x赋值为xp / \
// xp(r) xppr(b) ====> rotateLeft(x) x(r) xppr(b)
// \ /
// x(r) xp(r)
// 旋转之前先把x赋值为xp,通过旋转结果可以明了地看出为什么要把x赋值为xp
root = rotateLeft(root, x = xp); // 旋转后返回新的根节点
// 现在x是指向图2中 xp的位置
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// xpp(b) xp(b)
// / \ / \
// xp(r) xppr(b) ===> x(r) xpp(r)
// / \
// x(r) xppr(b)
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
// 旋转会返回新的根节点(根节点可能会在旋转后变化,也可能不变)
root = rotateRight(root, xpp);
}
}
}
}
else {
// xp 是红色的,且xp 是xpp 的右子树
if (xppl != null && xppl.red) {
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.left) {
// 之字形旋转,先进行右转
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
}
// x -> replacement
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
TreeNode<K,V> x) {
for (TreeNode<K,V> xp, xpl, xpr;;) {
// 这里的 root 是局部变量 root
if (x == null || x == root)
// 如果删除的是根节点,那么会设置 root = s (右子树最小节点),所以这种情况下 x != root
// x == root的情况: 1、上溯到根节点,直接返回,此时这棵树的所有路径少了一个黑色节点
// 2、 设置 x = root,退出循环
return root;
else if ((xp = x.parent) == null) {
// todo: xp == null 那么 x不就是根节点了吗? 根本不会进入到这里面? 每次旋转后 root都会指向新的根节点
// 什么情况下 root 不是根节点? 好像不存在这种情况 (出现并发删除时候可能会出现这种情况)
// xp == null 说明x 就是根节点,设置x的颜色为黑色
x.red = false;
return x;
}
else if (x.red) {
// replacement != p 时,x(replacement)的颜色一定是红色的,在这会把replacement的颜色变成黑色的
// x 的颜色为红色的,设置x的颜色为黑色即可
// 1、x不可能是删除的元素本身,因为如果删除的元素是红色的,不会调用这个方法
// 2、x可能是删除元素的左节点,或者右节点,此时已经使用x替换了删除元素的位置,只要把颜色设置为黑色即可
// 3、上溯过程中,如果x的颜色是红色的,设置为黑色的即可
x.red = false;
return root;
}
else if ((xpl = xp.left) == x) {
// x 是xp的左子树
// 走到这里说明 x是删除的元素本身而且颜色是黑色,且x 没有子节点
// 这时候需要分以下几种情况:
// 1、x的兄弟节点s是否是红色的
// 2、x的兄弟节点s是黑色,s是否有红色的孩子
// 3、x的兄弟节点s是黑色的,且没有红色的孩子,父节点是否是红色的
// 4、如果以上情况都不是,s设置为红色,然后上溯,最坏情况下,到达根节点,把根节点的另一个子节点变成红色的,
// 这样,整棵树的所有路径少了一个黑色的节点。上溯过程中也始终不会修改根节点的颜色,所以也不用维护根节点的颜色。
// xpr -> x的兄弟节点
if ((xpr = xp.right) != null && xpr.red) {
// 1、 x的兄弟节点是红色,通过旋转解决
// P(b) S(b) S(b)
// / \ 第一步 / \ 第二步 / \
// D(b) S(r) ====> P(r) SR(b) ====> P(b) SR(b)
// / \ / \ / \
// SL(b) SR(b) D(b) SL(b) D(r) SL(r)
// 这里只完成了上面的第一步
xpr.red = false; // 兄弟节点颜色变成黑色的
xp.red = true; // xp的颜色变成红色的
root = rotateLeft(root, xp); // 左旋
// 旋转后位置变了,重新赋值: xp = x.parent; xpr = xp.right
xpr = (xp = x.parent) == null ? null : xp.right;
}
if (xpr == null) // todo: 什么情景? x的颜色是黑色的,所以xpr不可能为空的
// (可能是出现并发删除时会有这种情况)
x = xp;
else {
TreeNode<K,V> sl = xpr.left, sr = xpr.right;
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
// ----- 上溯在这里处理 ----
// 2、x的兄弟节点xpr是黑色的,且xpr没有红色的孩子,把兄弟节点设置为红色,把 x 指向 xp 进行上溯
// 上面的 情景1 的第二步在这里得到解决 (xp是红色的,所以直接设置成黑色的即可)
xpr.red = true;
x = xp;
}
else {
// 3、S 的左节点是红色的,通过旋转和颜色变换把场景变成场景4
// P(b) P(b)
// / \ / \
// D(b) S(b) ====> D(b) SL(b)
// / \
// SL(r) S(R)
if (sr == null || !sr.red) { // 如果左右节点都是红色的,那么只需要进行单旋转就行了
if (sl != null)
// sl 的颜色设置为黑色的
sl.red = false;
xpr.red = true; // 把xpr颜色变成红的
root = rotateRight(root, xpr);
// xpr指向上图的SL位置
xpr = (xp = x.parent) == null ?
null : xp.right;
}
// 4. S的右节点颜色是红色的
// 原理:旋转变色后右子树黑色节点个数不变,左子树黑色节点个数多了一个
// P(b) S(b)
// / \ / \
// D(b) S(b) ====> P(b) SR(b)
// \ /
// SR(r) D(r)
// P(r) S(r)
// / \ / \
// D(b) S(b) ====> P(b) SR(b)
// \ /
// SR(r) D(r)
if (xpr != null) {
xpr.red = (xp == null) ? false : xp.red; // xp节点也可能是红的,所以设置成xp的颜色
if ((sr = xpr.right) != null)
sr.red = false; // 设置sr颜色为黑色的
}
if (xp != null) {
xp.red = false;// 设置xp颜色为黑色的,对应上图的P节点
root = rotateLeft(root, xp);
}
// 通过x = root 退出循环
x = root;
}
}
}
else { // symmetric
// 删除的元素在父节点的右边
if (xpl != null && xpl.red) {
xpl.red = false;
xp.red = true;
root = rotateRight(root, xp);
xpl = (xp = x.parent) == null ? null : xp.left;
}
if (xpl == null)
x = xp;
else {
TreeNode<K,V> sl = xpl.left, sr = xpl.right;
if ((sl == null || !sl.red) &&
(sr == null || !sr.red)) {
xpl.red = true;
x = xp;
}
else {
if (sl == null || !sl.red) {
if (sr != null)
sr.red = false;
xpl.red = true;
root = rotateLeft(root, xpl);
xpl = (xp = x.parent) == null ?
null : xp.left;
}
if (xpl != null) {
xpl.red = (xp == null) ? false : xp.red;
if ((sl = xpl.left) != null)
sl.red = false;
}
if (xp != null) {
xp.red = false;
root = rotateRight(root, xp);
}
x = root;
}
}
}
}
}
/**
* Recursive invariant check
*/
// 检查链表结构和红黑树结构是否正常
static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
tb = t.prev, tn = (TreeNode<K,V>)t.next;
// 检查 tb(tp) 和 t的双向连接是否正确
if (tb != null && tb.next != t)
return false;
// 检查 tn 和 t的双向连接是否正确
if (tn != null && tn.prev != t)
return false;
// 检查 tp 和 t 的节点指向是否正确
if (tp != null && t != tp.left && t != tp.right)
return false;
// 检查 tl 和 t的节点指向是否正确,并比较哈希值,tl的哈希值不能大于t的哈希值
if (tl != null && (tl.parent != t || tl.hash > t.hash))
return false;
// 检查 tr 和 t的节点指向是否正确,并比较哈希值,tr的哈希值不能小于t的哈希值
if (tr != null && (tr.parent != t || tr.hash < t.hash))
return false;
// 检查不能有两个连续的红色节点
if (t.red && tl != null && tl.red && tr != null && tr.red)
return false;
// 递归检查左子树 (使用的是中序遍历)
if (tl != null && !checkInvariants(tl))
return false;
// 递归检查右子树
if (tr != null && !checkInvariants(tr))
return false;
return true;
}
private static final sun.misc.Unsafe U;
private static final long LOCKSTATE;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = TreeBin.class;
LOCKSTATE = U.objectFieldOffset
(k.getDeclaredField("lockState"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/* ----------------Table Traversal -------------- */
/**
* 记录表、表的长度和当前遍历索引,用于必须在处理当前表之前处理已转发表的某个区域的遍历器。
* Records the table, its length, and current traversal index for a
* traverser that must process a region of a forwarded table before
* proceeding with current table.
*/
static final class TableStack<K,V> {
// 旧表的长度
int length;
// 遍历到旧表的索引位置
int index;
// 旧表
Node<K,V>[] tab;
TableStack<K,V> next;
}
/**
* 封装遍历方法,如containsValue;还可以作为其他迭代器和spliterator的基类。
* Encapsulates traversal for methods such as containsValue; also
* serves as a base class for other iterators and spliterators.
*
* 方法预先访问在迭代器构造时可以访问的每个仍然有效的节点。
* Method advance visits once each still-valid node that was
* reachable upon iterator construction.
* 它可能会遗漏一些在访问bin之后添加到bin中的内容,这是wrt一致性的保证。
* It might miss some that
* were added to a bin after the bin was visited, which is OK wrt
* consistency guarantees.
* 在可能持续调整大小的情况下维护此属性需要相当数量的簿记状态,
* 在易变访问中很难优化这些状态。尽管如此,遍历仍然保持了合理的吞吐量。
* Maintaining this property in the face
* of possible ongoing resizes requires a fair amount of
* bookkeeping state that is difficult to optimize away amidst
* volatile accesses. Even so, traversal maintains reasonable
* throughput.
*
* 通常情况下,迭代会逐箱遍历列表。但是,如果表已经调整了大小,那么以后的所有
* 步骤都必须遍历当前索引处的bin和在 (index + baseSize)索引处;等等来进一步调整大小。
* Normally, iteration proceeds bin-by-bin traversing lists.
* However, if the table has been resized, then all future steps
* must traverse both the bin at the current index as well as at
* (index + baseSize); and so on for further resizings. To
* 为了偏执地处理迭代器用户在线程之间的潜在共享,如果读取的表的边界检查失败,迭代将终止。
* paranoically cope with potential sharing by users of iterators
* across threads, iteration terminates if a bounds checks fails
* for a table read.
*/
static class Traverser<K,V> {
// 当前table,扩容会更新
Node<K,V>[] tab; // current table; updated if resized
// 上次遍历返回的节点
Node<K,V> next; // the next entry to use
// stack当前使用的TableStack, spare备份用过的TableStack以重新用
TableStack<K,V> stack, spare; // to save/restore on ForwardingNodes
// 当前表遍历的索引位置 (当前正在遍历的表)
int index; // index of bin to use next
// 初始表遍历的索引位置
int baseIndex; // current index of initial table
// 初始表的索引界限
int baseLimit; // index bound for initial table
// 初始表大小
final int baseSize; // initial table size
// containsValue()方法 -> new Traverser<K,V>(t, t.length, 0, t.length)
Traverser(Node<K,V>[] tab, int size, int index, int limit) {
this.tab = tab;
this.baseSize = size;
this.baseIndex = this.index = index;
this.baseLimit = limit;
this.next = null;
}
/**
* example:
* 创建 Traverser 时,table为 table1,遍历过程中 table扩容到 table4了,然后进行索引1 的遍历
*
* table1 table2 table3 table4
* length: 16 32 64 128
*
* 索引1的元素为: FWN FWN FWN 保存数据Node
*
* 1、 this.tab -> table1,获取tab 索引1的元素,该元素是 ForwardingNode,所以 this.tab -> table2,
* 然后创建 TableStack1 { tab = table1; length = 16; index = 1; next = null },stack = TableStack1;
*
* 2、 this.tab -> table2, 获取索引1的元素,该元素是 ForwardingNode,所以 this.tab -> table3,
* 然后创建 TableStack2 {tab = table2; length = 32; index = 1; next = TableStack1 }, stack = TableStack2;
*
* 3、 this.tab -> table3, 获取索引1的元素,该元素是 ForwardingNode,所以 this.tab -> table4,
* 然后创建 TableStack3 {tab = table3; length = 64; index = 1; next = TableStack2 }, stack = TableStack3;
*
* 4、 this.tab -> table4, 获取索引1的元素,成功获取到元素,此时stack = TableStack3 != null,调用 recoverState(128),
* index = index + TableStack3.length = 1 + 64 = 65, 65 < 128;
*
* 5、 this.tab -> table4, 获取索引65的元素,成功获取到元素(或者没有获取到元素),此时stack = TableStack3 != null,
* 调用 recoverState(128), index = 65 + 64 = 129, 129 > 128, 所以:
* n = 64; index = 1; tab = table3; next = TableStack2; TableStack3.next = null;
* -> stack = TableStack2 <- ; spare = TableStack3;
* index = 1 + 32 = 33, 33 < 64
*
* 6、 this.tab -> table3, 获取索引33的元素,该元素是 ForwardingNode,所以 this.tab -> table4, spare = TableStack3,所以
* 重用 TableStack3 {tab = table3; length = 64; index = 33; next = TableStack2 }, stack = TableStack3;
*
* 7、 this.tab -> table4, 获取索引33的元素,然后获取索引 33 + 64 = 96 的元素, 然后 96 + 64 > 128, 所以:
* n = 64; index = 33; tab = table3; next = TableStack2; TableStack3.next = null;
* -> stack = TableStack2 <- ; spare = TableStack3;
* index = 33 + 32 = 65, 65 > 64, 所以:
*
* n = 32; index = 1; tab = table2; next = TableStack1; TableStack2.next = TableStack3;
* -> stack = TableStack1 <- ; spare = TableStack2;
*
*
* 总结:
* table1 table2 table3
* length: 16 32 64
*
* 遍历table1索引1的元素,-> 遍历table2 索引1的元素 -> 遍历table3索引1、 1 + 32的元素
* -> 遍历 table2 索引 1 + 16 = 17 的元素 -> 遍历table3索引 17、 17 + 32 的元素
* -> 遍历table2 索引2的元素 .....
*/
/**
* 如果可能,则前进,返回下一个有效节点,如果没有,则返回null。
* Advances if possible, returning next valid node, or null if none.
*/
final Node<K,V> advance() {
Node<K,V> e;
// next 是上次遍历返回的元素
if ((e = next) != null)
// 遍历下一个元素
e = e.next;
for (;;) {
Node<K,V>[] t; int i, n; // must use locals in checks
if (e != null)
// 把 e 赋值给next,用于下次遍历,然后返回 e
return next = e;
// baseIndex >= baseLimit 超过限定的索引位置
// (n = t.length) <= (i = index) 超出表的索引位置,遍历完成
// i < 0 索引小于0
if (baseIndex >= baseLimit || (t = tab) == null ||
(n = t.length) <= (i = index) || i < 0)
// 设置next = null,并返回 null
return next = null;
// i = index
if ((e = tabAt(t, i)) != null && e.hash < 0) {
if (e instanceof ForwardingNode) {
// tab指向新的表
tab = ((ForwardingNode<K,V>)e).nextTable;
e = null;
// t = tab 上一个 table, i = index, n 上一个table的长度
pushState(t, i, n); // 保存当前表的遍历状态
// 继续循环, 索引位置不变,从下一个表去查找
continue;
}
else if (e instanceof TreeBin)
// 如果是 TreeBin,把第一个节点赋值给 e
e = ((TreeBin<K,V>)e).first;
else
e = null;
}
if (stack != null)
// n -> 新表的长度
recoverState(n); // 重新获取遍历状态
/**
* 虽然元素转移是从后往前进行transfer()的,但是有可能是多线程同时进行transfer(),因此当遇到
* ForwardingNode节点后,下一个索引的元素可能不是 ForwardingNode,因此出现stack != null之后,
* 遍历后续的索引位置,stack还可能为null
*/
// this.tab 不为初始化表的时候, stack 不会为null
else if ((index = i + baseSize) >= n)
// baseIndex + 1 然后赋值给 index
index = ++baseIndex; // visit upper slots if present 如果有的话,访问上面的槽
}
}
/**
* 保存遇到转发节点时的遍历状态。
* Saves traversal state upon encountering a forwarding node.
*/
private void pushState(Node<K,V>[] t, int i, int n) {
// 如果 spare 不为null的话,那么重新使用 spare,避免频繁创建对象,
// 扩容n次,只需要创建 n个 TableStack 对象就够了
TableStack<K,V> s = spare; // reuse if possible
if (s != null)
spare = s.next;
else
// 创建一个 TableStack
s = new TableStack<K,V>();
// 保存遍历状态
s.tab = t;
s.length = n;
s.index = i;
// s.next = stack -> next 指向上一个 TableStack (遍历过程中遇到多次扩容)
s.next = stack;
// stack 指向新的 TableStack
stack = s;
}
/**
* Possibly pops traversal state.
*
* @param n length of current table
*/
// 重新获取遍历状态
private void recoverState(int n) {
TableStack<K,V> s; int len;
// s = stack
// --> index = index + s.length <---, 大于等于n 说明超出了表的索引位置
// n 是当前表的长度, s.length是前一个表的长度。 比如:当前表的长度n 为32,前一个表的长度s.length = 16
// 读取的索引为 index = 3,那么使用新表读完索引3的元素后,还需要读取索引 3 + 16 的元素
while ((s = stack) != null && (index += (len = s.length)) >= n) {
// n 设置为上一个表的长度
n = len;
// index 设置为上一个表遍历的索引位置
index = s.index;
// tab 设置为上一个表,回到上一个表遍历
tab = s.tab;
s.tab = null;
// s.next 为s 的上一个 TableStack
TableStack<K,V> next = s.next;
// s.next 设置为 spare (用过的TableStack使用链表的形式保存起来,
// 遍历下一个索引的时候会一个接一个取出来使用)
s.next = spare; // save for reuse
// stack 设置上一个TableStack, 这个设置之后, stack 可以为 null
stack = next;
// 把 s 赋值给 spare,把TableStack 保存起来下次使用
spare = s;
}
// s = null 才会计算 index += baseSize
// 当初始表的下一个扩容表遍历完成之后,s才会为null。比如 初始表为 table1, 长度为16,
// 扩容表为table2,长度为32, 遍历索引1的元素时,需要遍历table2索引1、索引17的元素,
// table2的索引1、17元素遍历完成之后,s = null,然后开始遍历索引table1索引2的元素
// 回到初始表遍历的时候 s才会为 null
if (s == null && (index += baseSize) >= n)
// baseIndex + 1,并赋值给 index
index = ++baseIndex;
}
}
/**
* Base of key, value, and entry Iterators. Adds fields to
* 添加字段以支持iterator.remove.
* Traverser to support iterator.remove.
*/
static class BaseIterator<K,V> extends Traverser<K,V> {
final ConcurrentHashMap<K,V> map;
Node<K,V> lastReturned;
BaseIterator(Node<K,V>[] tab, int size, int index, int limit,
ConcurrentHashMap<K,V> map) {
super(tab, size, index, limit);
this.map = map;
// 先调用advance()遍历出第一个元素,以支持hasNext()方法
advance();
}
// 判断是否还有下个元素,接口 Iterator的方法
public final boolean hasNext() { return next != null; }
// 判断是否还有下个元素,接口 Enumeration的方法
public final boolean hasMoreElements() { return next != null; }
// 删除遍历到的元素
public final void remove() {
Node<K,V> p;
// lastReturned = null,抛出异常
if ((p = lastReturned) == null)
throw new IllegalStateException();
// 设置lastReturned = null
lastReturned = null;
// 删除节点
map.replaceNode(p.key, null, null);
}
}
// BaseIterator<K,V> extends Traverser<K,V>
// Iterator 接口中的其他方法已经在父类BaseIterator中实现了
static final class KeyIterator<K,V> extends BaseIterator<K,V>
implements Iterator<K>, Enumeration<K> {
KeyIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
super(tab, index, size, limit, map);
}
public final K next() {
Node<K,V> p;
// p = next, next = null,说明没有元素了
if ((p = next) == null)
throw new NoSuchElementException();
// 获取key值
K k = p.key;
// 记录此次返回的节点,以支持删除
lastReturned = p;
// 获取遍历的下一个元素
advance();
return k;
}
// 直接调用next()
public final K nextElement() { return next(); }
}
static final class ValueIterator<K,V> extends BaseIterator<K,V>
implements Iterator<V>, Enumeration<V> {
ValueIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
super(tab, index, size, limit, map);
}
public final V next() {
Node<K,V> p;
// p = next,next = null,说明没有元素了,抛出异常
if ((p = next) == null)
throw new NoSuchElementException();
// 获取value 值
V v = p.val;
// 记录此次返回的节点,以支持删除
lastReturned = p;
// 获取遍历的下一个元素
advance();
return v;
}
// Enumeration接口的方法
public final V nextElement() { return next(); }
}
static final class EntryIterator<K,V> extends BaseIterator<K,V>
implements Iterator<Map.Entry<K,V>> {
EntryIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
super(tab, index, size, limit, map);
}
public final Map.Entry<K,V> next() {
Node<K,V> p;
// p = next, next = null,说明没有元素了
if ((p = next) == null)
throw new NoSuchElementException();
// 获取 key和 value
K k = p.key;
V v = p.val;
// 记录此次返回的节点,以支持删除
lastReturned = p;
// 获取遍历的下一个元素
advance();
// 创建Entry节点,传入map以支持value的修改
// HashMap直接返回Node,这两个map的Node节点都有实现 Map.Entry<K,V>接口,
// 但是ConcurrentHashMap的Node不支持value的修改,需要通过 map.put(key, value)
// 方法进行修改,因为ConcurrentHashMap的更新操作需要获取锁
return new MapEntry<K,V>(k, v, map);
}
}