这个Map你肯定不知道,毕竟存在感确实太低了。 (下)

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
简介: 这个Map你肯定不知道,毕竟存在感确实太低了。 (下)

畅游源码-REMOVE


接着再看最后一个 remove 方法:

image.png

首先,标号为 ① 的地方,你想到了什么东西?

我看到这个 modCount 可太亲切了。围绕着这个玩意,我前前后后大概写了有 3w 多字的文章吧:

image.png

是为了抛出 ConcurrentModificationException 服务的。

这里体现的是 fast-fail 的思想。

关于这个异常最经典的一个面试题就是:ArrayList 如果一边遍历,一边删除,会出现什么情况?

什么?你不会?我也不回答了。

假粉丝,请你回去等通知吧。

标号为 ② 的地方,把 i 和 i+1 的位置都置为 null。也就是把 key 和对应的 value 都置为 null。

执行完标号为 ② 的地方, remove 的操作也就完成了。

那么按理来说方法就应该结束了。对吗?

image.png

你想一想我之前的这个图片:

image.png

发现问题了吗?

如果这个时候我来查询 key2,而 key2 经过 hash 方法后计算出来的 i 还是 2,而对应位置上的值是 null:

image.png

这个时候你告诉我 key2 查不到,返回一个 null 给我?

key2,啪,没了!


image.png

所以,标号为 ③ 的地方就是为了解决这个问题的。

java.util.IdentityHashMap#closeDeletion

image.png

你看这个方法标号为 ① 的地方,自己都说了:

朋友,因为我们这个结构是一个圆,这个方法比较混乱。做好心理准备。

然后就是一个异常复杂的 if 判断。

这个我是看懂了,但是属于只可意会不可言传的那种,所以就不给大家分析了。大家有兴趣的自己去看看。

只要你抓准了它的存储机制和方法功能,理解起来应该不算很费劲。

image.png

再看标号为 ② 的地方,理解起来就很容易了,把之前由于 hash 冲突导致的位置偏移的数据,一个个的往前挪:

image.png

意思就是上面图片的意思。

先把 key1 从 i=2 的位置移走。然后把 i=4 的 key2 往前移动 2 位。

这样,下次来查询 key2 的时候,就能得到正确的返回了。

这里留下一个疑问,假设下面这个场景:

image.png

key1 和 key2 是有 hash 冲突的,但是 key3 是正常的。

那么移除掉 key1 之后的图应该是这样的:

image.png

代码是怎么控制或者说怎么知道 key2 和 key1 是有冲突的,所以移走 key1 之后,需要把 key2 往前移动。而 key3 和 key2 是没有关系的,所以 key3 放着不动。

答案其实就藏在 closeDeletion 方法的源码里面,就看你有没有彻底理解这个方法了。

好了,到这里关于 identityHashMap 增删改查我们就分享完毕了。

老规矩,源码导读,点到为止。

就像传统功夫,都是点到为止。年轻人,不讲武德,耗子尾汁...

image.png

马老师可真是我最近一段时间的快乐源泉啊。

咦,偏了偏了,说编程呢,怎么说到马老师那边去了。

难道我不经意间发现了:万物皆可马保国定律?


identityHashCode的错误使用


前面说了,IdentityHashMap 的核心点在于 System.identityHashCode 方法。

说到这个 identityHashCode 我又想到了曾经在 Dubbo 中的看到的一段源码。

位于一致性哈希负载均衡算法中:

org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance#doSelect

image.png

上面的源码是 2.7.8 版本。

假设有五个可用的服务提供者,这里的 invokers 集合里面装的就是一个个服务提供者。

然后调用了 invokers ,也就是 list 的 hashCode 方法。

因为一致性哈希的负载均衡的思想就是当服务发生了上下线之后,我们需要对哈希环进行调整。

如果服务没有发生上下线,那么是不需要进行哈希环调整的。

具体到这个 list 来说就是:

当 list 里面的元素发生了变化,那么说明有服务上下线的情况发生。

至于你装元素的 list 是否和原来的不一样,那我是不关心的。

所以作者在这里还写了一个备注:我们应该只注意 list 里面的元素就可以了。

言外之意就是我刚刚说的:装元素的 list 是否发生了变化,我是不关心的。

按照开源框架的尿性,这地方专门写了一行注释,说明这个地方曾经是有问题的。

那我们看看这个地方的提交记录:

image.png

image.png

你看,原来的代码是 System.identityHashCode 方法。

后来修改为调用 list 的 hashCode 方法。

单单看着一行代码,我们就知道,之前的代码是关注 list 这个容器了,导致了某些 bug 的出现。

具体什么原因,我们可以看看这次提交对应的 pr:

也就是编号为 5429 的 issue:

https://github.com/apache/dubbo/issues/5429

image.png

哎呀,我去,这谁啊?看着眼熟啊?这不就是 why 哥吗?这不是巧了吗,这不是?

是的,这个 bug 就是我发现并提出的对应的 issue。

image.png

而且这个 bug 其实是非常好发现的,只要你把环境一搭,代码一跑,场景一模拟。是个必现的问题。

而产生这个 bug 的原因,可谓是蝴蝶效应。在离这段源码很远的,毫不相干的一次需求中,不知不觉的就影响到了这段代码。

而且连开发者自己都不知道,自己的修改会影响到一致性哈希负载均衡算法。所以,根本也就谈不上什么测试用例了。

如果你想更进一步了解这个 bug 的来龙去脉。可以看看这篇文章:

《够强!一行代码就修复了我提的Dubbo的Bug》

如果你想更进一步的了解 Dubbo 的负载均衡策略,那可以看看这篇文章:

《吐血输出:2万字长文带你细细盘点五种负载均衡策略。》

好了,那么这次的文章就到这里啦。给大家分享了一个冷门的、"学了没多大卵用" 的 IdentityHashMap。

你要是不喜欢下面的荒腔走板环节的话,也请记得拉到文章的最后。留言、点赞、在看、转发、赞赏,随便来一个就行。你要是都安排上,我也不介意。


荒腔走板


最近项目组接到了一个工期特别紧张的项目。

所以刚刚过去的周末我加了两天的班。周六晚上把流程走通之后,已经快是 22 点了。

之前预约了安装家电的师傅,刚好也是周六。

所以只有女朋友一个人去家那边,边打扫卫生,边等着安装师傅。

安装师傅全部弄好之后也是 19 点之后了。

因为我从公司到家特别的近。女朋友觉得我也差不多该下班了,于是决定就在家里等我,然后一起从家里回到租住的小区。

结果一等就是 2 个多小时。

我下班之后,马上打车到小区。

下午没有吃饭,工作也比较劳累,坐在车上,一阵疲倦的感觉袭来。

但是在小区门口刷门禁卡的时候,我一抬头,门口写着:欢迎回家。

那一刻,我突然觉得好暖啊,甚至还有一丝丝的感动。

走在小区的路上,感觉一切都是这么的可爱。

因为这个家,真的是属于自己的家,用自己一手一脚挣出来的钱堆出来的。

此时此刻,家里还有一个人,开着灯,在等着我回家。

之前我从来没有这样的感觉过,这是一种非常神奇的感觉。

到家之后,由于家具还没有准备好,我看到女朋友在地上铺着一个泡沫垫子,坐在上面,靠在墙上,通过手机看着综艺。

她起来抱了抱我,说:你终于回来啦。今天的事可真是多。

我们一起站在空荡荡的客厅中间。

那一刻,家的含义,家的感觉,从来没有这么具体过。


最后说一句(求关注)


才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在留言区提出来,我对其加以修改。 感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

我是 why,一个被代码耽误的文学创作者,不是大佬,但是喜欢分享,是一个又暖又有料的四川好男人。

欢迎关注我呀。


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
存储 API
这个Map你肯定不知道,毕竟存在感确实太低了。 (中)
这个Map你肯定不知道,毕竟存在感确实太低了。 (中)
129 0
这个Map你肯定不知道,毕竟存在感确实太低了。 (中)
|
存储 Dubbo Java
这个Map你肯定不知道,毕竟存在感确实太低了。 (上)
这个Map你肯定不知道,毕竟存在感确实太低了。 (上)
96 0
这个Map你肯定不知道,毕竟存在感确实太低了。 (上)
|
6月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
6月前
|
存储 JavaScript 前端开发
JavaScript进阶-Map与Set集合
【6月更文挑战第20天】JavaScript的ES6引入了`Map`和`Set`,它们是高效处理集合数据的工具。`Map`允许任何类型的键,提供唯一键值对;`Set`存储唯一值。使用`Map`时,注意键可以非字符串,用`has`检查键存在。`Set`常用于数组去重,如`[...new Set(array)]`。了解它们的高级应用,如结构转换和高效查询,能提升代码质量。别忘了`WeakMap`用于弱引用键,防止内存泄漏。实践使用以加深理解。
79 3
|
3月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
3月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
4月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
4月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。
|
4月前
|
算法 Java 索引
【Java集合类面试四】、 描述一下Map put的过程
这篇文章详细描述了HashMap中put操作的过程,包括首次扩容、计算索引、插入数据以及链表转红黑树和可能的再次扩容。
【Java集合类面试四】、 描述一下Map put的过程
|
4月前
|
存储