继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java ConcurrentHashMap:JDK 1.7 与 1.8 的实现区别解析

我就是渣哥
关注TA
已关注
手记 65
粉丝 1
获赞 3

原文来自于:https://zha-ge.cn/java/36

Java ConcurrentHashMap:JDK 1.7 与 1.8 的实现区别解析

大家好,今天就来聊聊那个让人又爱又恨的ConcurrentHashMap。说实话,Java集合天团里它算得上“老江湖”了。不过,隐藏的“江湖恩怨”你了解多少?尤其是JDK 1.7跟1.8这两代的实现区别,不扒一扒都对不起我掉过的那些头发。

那年的并发Map

故事还得从我刚入坑Java的那年说起。老板一脸坏笑地扔给我一句:“你写的Map线程安全不?”我心想,这事小菜一碟不是有Hashtable嘛!后来一查,哎呦喂,同步整得太死太粗暴,每次put/get都锁全表。这性能,连隔壁的PHP同事都忍不下去了。

后来我发现了ConcurrentHashMap,据说是拿锁分段,支持并发,非常香。到底有啥高绝招?JDK 1.7 时代它的核心是“分段锁”机制。想象一下,数据结构里有好几个Segment,每块都有自己的锁。只要两个线程想改不同Segment,互不打扰,各回各家各找各妈。代码大致长这样:

int hash = hash(key);
int segmentIndex = (hash >>> segmentShift) & segmentMask;
Segment<K,V> segment = segments[segmentIndex];
segment.put(key, value);   // 分段锁住

是不是很“分田分地真忙”那味?一看代码,果然每个Segment都是重Sync对象。

世道在变,ConcurrentHashMap也变

JDK 1.8一来,这货直接玩了个大的:分段锁说拜拜,全新链表+Node+CAS+红黑树组合拳(对,树!你没听错)。

写入时直接对Node数组某个下标table[i]加锁(其实是Synchronized),锁的粒度进一步小了。更绝的是,用CAS搞定了大部分写操作的小竞争。碰到桶里元素太多?那就变红黑树,查找速度飞起。

关键逻辑像这样:

Node<K,V>[] tab = table;
int i = (n - 1) & hash;
Node<K,V> f = tabAt(tab, i);
if (f == null) {
    if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
        // 插入成功
} else {
    // 某些情况下,加锁链表/红黑树节点
    synchronized (f) {
        // ......
    }
}

一句话,锁得更细,连“争抢”都变斯文了。

踩坑瞬间

说实话,升级到JDK 1.8后我还挺飘的,觉得世界和平了。直到有次新上线的服务突然卡成狗。排查一圈,死活找不到瓶颈。最后还是老同事提醒:

“你确定所有put操作都原子了吗?树化过程有检查过没?”

后来查源码,发现1.8的树化和扩容其实会瞬时锁住链表/节点,比起1.7多个Segment锁,有时候激烈并发反而踩进性能坑。还有同事吐槽:hash冲突严重时,红黑树优化反而救不了“猪一样”的Key分布。

经验启示

程序员的世界没有银弹,和Map斗智斗勇的日子总结下来就是:

  • 提升并发:1.8理论上更细粒度,但要控制Key分布和负载。
  • 大批量写入?提前规划一下容量和补齐扩容槽,别让rehash业务高峰来搅局。
  • 有性能抖动或卡顿,别光盯业务逻辑,仔细数数Map操作热点和Key分布。
  • 线程池+并发Map,别瞎摆,压力测试必不可少。

最后友情提示,别迷信“新版必优”,场景合适才是真章。不然,下一个掉头发的就是你。


如果你也有ConcurrentHashMap的血泪史,欢迎评论区唠唠呗。今天就到这,下次八卦别忘了带上瓜子,我们江湖再见!

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP