记录学习ConcurrentHashMap
看下定义的常量
// 最大容量
private static final int MAXIMUM_CAPACITY = 1 << 30;
//默认容量
private static final int DEFAULT_CAPACITY = 16;
//数组的最大长度值
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//默认并发是16 ,16就是瓶颈
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
//hahsmap也有 装载因子
private static final float LOAD_FACTOR = 0.75f;
//变树的临界值
static final int TREEIFY_THRESHOLD = 8;
//变回来的临界值,如果定义8,会频繁变来变去
static final int UNTREEIFY_THRESHOLD = 6;
//
static final int MIN_TREEIFY_CAPACITY = 64;
//桶中的某个链表转换为树结构时,concurrentHashMap 所需要的最低容量值
private static final int MIN_TRANSFER_STRIDE = 16;
//帮助扩容时候的参数
private static int RESIZE_STAMP_BITS = 16;
//帮助扩容时候的参数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
//控制扩容
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
//fowrd的hash值
static final int MOVED = -1; // hash for forwarding nodes
// tree的hash值
static final int TREEBIN = -2; // hash for roots of trees
//reservation node的hash值
static final int RESERVED = -3; // hash for transient reservations
//普通节点的散列位
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
看最为关键put的源码
final V putVal(K key, V value, boolean onlyIfAbsent) {
//和hashmap不一样,不允许null值
if (key == null || value == null) throw new NullPointerException();
// 计算key的hash值
int hash = spread(key.hashCode());
int binCount = 0;
// cas 自旋
for (Node[] tab = table;;) {
Node f; int n, i, fh;
// 没有的话 进行初始化,初始化下面看源码
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//找到数组的节点,没有得话,添加头节点 ,用的是cas
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//牛逼的地方,判断节点是否在被扩容,如果是的话 ,一起扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
//前面两种都不是 那就是既不在扩容,也不是新的,那就锁住这颗树或链表,往里面加数据,如果遇到8就变树,和hashmap很相似
else {
V oldVal = null;
synchronized (f) {
//再次取出要存储的位置的元素,跟前面取出来的比较,cas
if (tabAt(tab, i) == f) {
//大于0,
if (fh >= 0) {
binCount = 1;
//遍历
for (Node e = f;; ++binCount) {
K ek;
//hash值一样 value一样,就替换
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
//当使用ifabsent方法的时候,没有才会放入
if (!onlyIfAbsent)
e.val = value;
break;
}
Node pred = e;
//其他就是没找到相同的,为空才设置值(为空意思便利到最后了),最后尾部加入这个新的值
if ((e = e.next) == null) {
pred.next = new Node(hash, key,
value, null);
break;
}
}
}
//如果是红黑树
else if (f instanceof TreeBin) {
Node p;
binCount = 2;
//调用putTreeVal方法
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
//变树,判断数量TREEIFY_THRESHOLD
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
看initTable的方法###
//sizeCtl 很关键,用来判断
//用来控制表初始化和扩容的,默认值为0
// 小于0表示正在初始化或resize
private final Node[] initTable() {
Node[] tab; int sc;
//为nul,未初始化,进入while循环
while ((tab = table) == null || tab.length == 0) {
//小于0说面在扩张
if ((sc = sizeCtl) < 0)
//等呗
Thread.yield(); // lost initialization race; just spin
//初始化,并标志为-1,正在初始化
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
////指定了大小的时候就创建指定大小的数组,否则默认
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node[] nt = (Node[])new Node,?>[n];
table = tab = nt;
//减去四分之一
sc = n - (n >>> 2);
}
} finally {
//四分之三的值
sizeCtl = sc;
}
break;
}
}
//返回table
return tab;
}
看下get
//这个就很简单了,找table的位置,然后便利节点
public V get(Object key) {
Node[] tab; Node e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
看下最有意思的helpTransfer 方法###
final Node[] helpTransfer(Node[] tab, Node f) {
Node[] nextTab; int sc;
// 如果 table 不是空 且 node 节点是转移类型,(不是很明白,后面再看下),下个table不为空,去帮助
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode)f).nextTable) != null) {
//扩容标志,下面看源码
int rs = resizeStamp(tab.length);
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {//老朋友,表示扩容
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
//退出关键点,这个if应该代表是扩容结束
break;
// 增加线程扩容,扩容
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
//help
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
//上面用到的,moved义遇到,意思在扩容,连接了两个table?,key vle都是null,且hash值为-1
ForwardingNode(Node[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
transfer
private final void transfer(Node[] tab, Node[] nextTab) {
int n = tab.length, stride;
//计算单个线程允许处理的最少table桶首节点个数,不能小于 16,和cpu也有关系?下次看,先留着
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range
//nextable 为空,new个新table
if (nextTab == null) { // initiating
try {
@SuppressWarnings("unchecked")
Node[] nt = (Node[])new Node,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
// 遍历index 从后面开始
transferIndex = n;
}
int nextn = nextTab.length;
//定义一个转移节点
ForwardingNode fwd = new ForwardingNode(nextTab);
boolean advance = true;
boolean finishing = false; // to ensure sweep before committing nextTab
for (int i = 0, bound = 0;;) {
Node f; int fh;
while (advance) {
int nextIndex, nextBound;
if (--i >= bound || finishing)
advance = false;
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
if (finishing) {
nextTable = null;
table = nextTab;
sizeCtl = (n << 1) - (n >>> 1);
return;
}
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
i = n; // recheck before commit
}
}
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
else if ((fh = f.hash) == MOVED)
advance = true; // already processed
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
Node ln, hn;
if (fh >= 0) {
int runBit = fh & n;
Node lastRun = f;
for (Node p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
if (runBit == 0) {
ln = lastRun;
hn = null;
}
else {
hn = lastRun;
ln = null;
}
for (Node p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node(ph, pk, pv, ln);
else
hn = new Node(ph, pk, pv, hn);
}
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof TreeBin) {
TreeBin t = (TreeBin)f;
TreeNode lo = null, loTail = null;
TreeNode hi = null, hiTail = null;
int lc = 0, hc = 0;
for (Node e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode p = new TreeNode
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
++lc;
}
else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin(hi) : t;
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}