浅谈ConcurrentHashMap

}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}

屏幕快照 2021-04-06 上午10.41.58.png

第一步做了一个校验,key或value为null都会抛出异常。 然后获取hash值,初始化binCount用于记录链表长度。

屏幕快照 2021-04-06 上午11.18.08.png

这里是个无限循环。
先判断table有没有初始化,没有就进行初始化。
(n - 1) & hash计算出插入下标,如果这个位置没有数据,直接放进去。
(fh = f.hash) == MOVED 如果其他线程在扩容,帮助其扩容。

// 有冲突后,进去else
synchronized (f) { // 锁住当前头节点
if (tabAt(tab, i) == f) { // 再次确认下标位置是否是f节点
if (fh >= 0) { // 头节点hash值 > 0说明是链表
binCount = 1;
for (Node<K,V> e = f;; ++binCount) { // 遍历
K ek;
// 如果key相同,则判断是否覆盖旧值
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent) // 默认情况下,覆盖旧值
e.val = value;
break;
}
Node<K,V> pred = e;
// 插到最后面
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key, value, null);
break;
}
}
}
else if (f instanceof TreeBin) { // 如果是红黑树,用红黑树的方式插数据
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}

屏幕快照 2021-04-06 下午3.33.56.png

最后判断链表长度是否达到8。
如果达到8,判断是否需要对数组扩容,数组长度小于64需要进行扩容。
如果数组长度大于64,就把链表转化为红黑树。

2.2 get()

public V get(Object key) {
Node<K,V>[] tab; Node<K,V> 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;
}

get 方法没有加锁!

  1. 空table,直接返回null;
  2. 计算hash值,找到相应的bucket位置,为node节点直接返回value,否则返回null
    3 总结
    ====
    ConcurrentHashMap 比起 HashMap 加上了锁机制,效率虽然差些,但多线程下是安全的,不会有死循环问题。对比HashTable,它只是锁住了需要访问的某个“桶”,所以效率比HashTable好上不少。
    各位可以视情况选择使用 HashMap 或 ConcurrentHashMap。

最后我想说

为什么很多程序员做不了架构师?
1、良好健康的职业规划很重要,但大多数人都忽略了
2、学习的习惯很重要,持之以恒才是正解。
3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展
4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。

金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…**
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值