为了账号安全,请及时绑定邮箱和手机立即绑定

听说过 HashMap 多线程的血泪史吗?

标签:
Java JavaScript

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

听说过 HashMap 多线程的血泪史吗?

还记得刚学 Java 的时候,心里总觉得 HashMap 好用得不行,插入、查找都飞快,容量伸缩还能自动安排。结果——等我真把它拉到多线程场景,差点跪着给老板写道歉信。

说出来你可能不信,我就是那种天真地以为“反正 Java 自带的,肯定线程安全”的理工直男。下面,开聊!


若有人问多线程加速,HashMap 你敢用吗?

场景是这样的:业务量上来了,老板说多开线程嘛,大家一起 put,效率肯定高!
我一想对啊,把 HashMap 搬出来,N 个线程协作,结果有一天线上报警——

  • 什么 key 居然找不到了!
  • 某些 value 被莫名其妙覆盖。
  • 更绝的是,偶尔居然死循环,CPU 飙飞天。

线上问题现场仿佛 CSI——一堆未解之谜。


踩坑瞬间

来个(痛苦)回忆杀:

比如有一段类似的代码:

map.put(key, value); 
// 省略了一堆同步保护

看起来平平无奇,但只要多个线程同时执行上面的操作,HashMap 内部的数据结构就会乱炸!

  • 有时 key 会丢失。
  • 有些槽位数据会混乱。
  • 最可乐的:JDK 8 以前,rehash 那一刻,链表可能自我拼接成了环……嗯?线性结构成了环形大冒险?查找 key 时 map.get() 直接死循环,主线程卡死!

那种“程序假死但不报错”的恐怖你体会过吗?就像地狱笑话:老板问你怎么查日志,你只能说“emmm……卡住了,日志没打印”。


当年我是怎么醒悟的

首先,翻官方文档——HashMap 明明脸都写着“非线程安全”,Javadoc 早就友情提示。只是我太年轻了!

然后追源码,才发现 put 操作其实是:

  • 新建 Entry 节点;
  • 塞到对应 bucket;
  • 如果碰上扩容、rehash……线程打架时可能你拿着旧指针,他拽着新数据,谁都不认谁;
  • 链表如何并发插入?老大难!(见鬼的环,真能恶心死人)

解决吗?有办法。

多线程下的救命稻草

  • 要么用 ConcurrentHashMap,天生好斗分段锁,线程见面也能好好说话了;
  • 或者你整个 Collections.synchronizedMap(oldHashMap),自己在外面加大锁;
  • 再不济,自己写 synchronized 块,然而锁得死死的,性能哭晕厕所。

直接贴个代码镇楼:

Map<K,V> syncMap = Collections.synchronizedMap(new HashMap<>()); 
// 或者直接上 ConcurrentHashMap
Map<K,V> concurrentMap = new ConcurrentHashMap<>();

经验启示

吐血整理踩坑经验,愿后来人少写几行加班代码:

  • HashMap 本身绝不适合多线程并发操作。
  • 多线程推荐:
    • ConcurrentHashMap(标配);
    • Collections.synchronizedMap(效率低下,慎用);
    • 显式同步(写起来丑,慎用)。
  • 死循环、数据丢失之类的锅 HashMap 背的起,但你得长点心。
  • 多看 Javadoc!多看源码!千万别想当然。
  • 日常实在太苦——记得多喝水,少加班😏。

写在最后

HashMap 这个家伙,单线程下妥妥好用,一上多线程就暴躁成渣。后来我每次用到它,就像见到一位不靠谱的前任,眉头一皱总得加点 Concurrent。

程序员的成长,都是这些血泪踩坑换来的。发文纪念那年那月被环形链表支配的恐惧。
各位看官,别重蹈我的覆辙哈!下次遇到“线程安全”,别忘了多留个心眼。

——码字收尾的程序猿

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消