一、put方法
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
* <p>The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
// key 和 value 都不能为空
if (key == null || value == null) throw new NullPointerException();
// 计算hash值
int hash = spread(key.hashCode());
// 用于记录相应链表的长度
int binCount = 0;
// 遍历数组
for (ConcurrentHashMap.Node<K,V>[] tab = table;;) {
ConcurrentHashMap.Node<K,V> f; int n, i, fh;
// 如果数组不存在 或数组长度为0 初始化表
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {// 如果数组当前位置为null
// 用CAS操作将值放入
if (casTabAt(tab, i, null,
new ConcurrentHashMap.Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// 如果table正在扩容 则得到扩容后的table 然后在重新开始一个循环
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// 到这里说明找到了key hash后对应的table,并且table上有其他node的存在
V oldVal = null;
// 把这个找到的node加上同步锁,防止并发出现的问题,如果其他key put进来的时候也对应这个tab则堵塞在这里
synchronized (f) {
// 再次用cas确认索引i上的table为我们找到的node,如果不是的话则这个node被修改,直接释放锁进入下一个循环
if (tabAt(tab, i) == f) {
// 如果目标table的第一个node的哈希值大于等于0,则是链式结构,走链表查找,反之走红黑树查找
if (fh >= 0) {
// 标记点
binCount = 1;
// 如果遍历元素的哈希值与需要插入目标key的哈希值相同,并且值也相同,则插入的是重复key的元素
for (ConcurrentHashMap.Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
// 如果onlyIfAbsent为false的话,则替换为新value,否则不修改
if (!onlyIfAbsent)
e.val = value;
break;
}
// 循环直到最后一个node节点的key都不是我们想要插入的key
ConcurrentHashMap.Node<K,V> pred = e;
if ((e = e.next) == null) {
//在尾部添加一个新节点,break循环
pred.next = new ConcurrentHashMap.Node<K,V>(hash, key,
value, null);
break;
}
}
}
//该节点属于红黑树的子节点,进行树操作
else if (f instanceof ConcurrentHashMap.TreeBin) {
ConcurrentHashMap.Node<K,V> p;
binCount = 2;
if ((p = ((ConcurrentHashMap.TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
// 如果node节点不为0
if (binCount != 0) {
// 如果node大于或者等于8,则转为红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//进行扩容判断
addCount(1L, binCount);
return null;
}
二、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;
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final ConcurrentHashMap.Node<K,V>[] (() {
ConcurrentHashMap.Node<K,V>[] tab; int sc;
// 通过循环判断
while ((tab = table) == null || tab.length == 0) {
// sizeCtl小于0 判断是否有线程占用 如果有 则让出cpu,进入就绪状态 默认值为0 -1指的是正在初始化
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {//CAS操作 将sc的值修改为1
try {
if ((tab = table) == null || tab.length == 0) {
// 如果 sizeCtl>0 初始化大小为sizeCtl,否则初始化大小为16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
ConcurrentHashMap.Node<K,V>[] nt = (ConcurrentHashMap.Node<K,V>[])new ConcurrentHashMap.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;
}