畅游源码-REMOVE
接着再看最后一个 remove 方法:
首先,标号为 ① 的地方,你想到了什么东西?
我看到这个 modCount 可太亲切了。围绕着这个玩意,我前前后后大概写了有 3w 多字的文章吧:
是为了抛出 ConcurrentModificationException 服务的。
这里体现的是 fast-fail 的思想。
关于这个异常最经典的一个面试题就是:ArrayList 如果一边遍历,一边删除,会出现什么情况?
什么?你不会?我也不回答了。
假粉丝,请你回去等通知吧。
标号为 ② 的地方,把 i 和 i+1 的位置都置为 null。也就是把 key 和对应的 value 都置为 null。
执行完标号为 ② 的地方, remove 的操作也就完成了。
那么按理来说方法就应该结束了。对吗?
你想一想我之前的这个图片:
发现问题了吗?
如果这个时候我来查询 key2,而 key2 经过 hash 方法后计算出来的 i 还是 2,而对应位置上的值是 null:
这个时候你告诉我 key2 查不到,返回一个 null 给我?
key2,啪,没了!
所以,标号为 ③ 的地方就是为了解决这个问题的。
java.util.IdentityHashMap#closeDeletion
你看这个方法标号为 ① 的地方,自己都说了:
朋友,因为我们这个结构是一个圆,这个方法比较混乱。做好心理准备。
然后就是一个异常复杂的 if 判断。
这个我是看懂了,但是属于只可意会不可言传的那种,所以就不给大家分析了。大家有兴趣的自己去看看。
只要你抓准了它的存储机制和方法功能,理解起来应该不算很费劲。
再看标号为 ② 的地方,理解起来就很容易了,把之前由于 hash 冲突导致的位置偏移的数据,一个个的往前挪:
意思就是上面图片的意思。
先把 key1 从 i=2 的位置移走。然后把 i=4 的 key2 往前移动 2 位。
这样,下次来查询 key2 的时候,就能得到正确的返回了。
这里留下一个疑问,假设下面这个场景:
key1 和 key2 是有 hash 冲突的,但是 key3 是正常的。
那么移除掉 key1 之后的图应该是这样的:
代码是怎么控制或者说怎么知道 key2 和 key1 是有冲突的,所以移走 key1 之后,需要把 key2 往前移动。而 key3 和 key2 是没有关系的,所以 key3 放着不动。
答案其实就藏在 closeDeletion 方法的源码里面,就看你有没有彻底理解这个方法了。
好了,到这里关于 identityHashMap 增删改查我们就分享完毕了。
老规矩,源码导读,点到为止。
就像传统功夫,都是点到为止。年轻人,不讲武德,耗子尾汁...
马老师可真是我最近一段时间的快乐源泉啊。
咦,偏了偏了,说编程呢,怎么说到马老师那边去了。
难道我不经意间发现了:万物皆可马保国定律?
identityHashCode的错误使用
前面说了,IdentityHashMap 的核心点在于 System.identityHashCode 方法。
说到这个 identityHashCode 我又想到了曾经在 Dubbo 中的看到的一段源码。
位于一致性哈希负载均衡算法中:
org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance#doSelect
上面的源码是 2.7.8 版本。
假设有五个可用的服务提供者,这里的 invokers 集合里面装的就是一个个服务提供者。
然后调用了 invokers ,也就是 list 的 hashCode 方法。
因为一致性哈希的负载均衡的思想就是当服务发生了上下线之后,我们需要对哈希环进行调整。
如果服务没有发生上下线,那么是不需要进行哈希环调整的。
具体到这个 list 来说就是:
当 list 里面的元素发生了变化,那么说明有服务上下线的情况发生。
至于你装元素的 list 是否和原来的不一样,那我是不关心的。
所以作者在这里还写了一个备注:我们应该只注意 list 里面的元素就可以了。
言外之意就是我刚刚说的:装元素的 list 是否发生了变化,我是不关心的。
按照开源框架的尿性,这地方专门写了一行注释,说明这个地方曾经是有问题的。
那我们看看这个地方的提交记录:
你看,原来的代码是 System.identityHashCode 方法。
后来修改为调用 list 的 hashCode 方法。
单单看着一行代码,我们就知道,之前的代码是关注 list 这个容器了,导致了某些 bug 的出现。
具体什么原因,我们可以看看这次提交对应的 pr:
也就是编号为 5429 的 issue:
https://github.com/apache/dubbo/issues/5429
哎呀,我去,这谁啊?看着眼熟啊?这不就是 why 哥吗?这不是巧了吗,这不是?
是的,这个 bug 就是我发现并提出的对应的 issue。
而且这个 bug 其实是非常好发现的,只要你把环境一搭,代码一跑,场景一模拟。是个必现的问题。
而产生这个 bug 的原因,可谓是蝴蝶效应。在离这段源码很远的,毫不相干的一次需求中,不知不觉的就影响到了这段代码。
而且连开发者自己都不知道,自己的修改会影响到一致性哈希负载均衡算法。所以,根本也就谈不上什么测试用例了。
如果你想更进一步了解这个 bug 的来龙去脉。可以看看这篇文章:
如果你想更进一步的了解 Dubbo 的负载均衡策略,那可以看看这篇文章:
好了,那么这次的文章就到这里啦。给大家分享了一个冷门的、"学了没多大卵用" 的 IdentityHashMap。
你要是不喜欢下面的荒腔走板环节的话,也请记得拉到文章的最后。留言、点赞、在看、转发、赞赏,随便来一个就行。你要是都安排上,我也不介意。
荒腔走板
最近项目组接到了一个工期特别紧张的项目。
所以刚刚过去的周末我加了两天的班。周六晚上把流程走通之后,已经快是 22 点了。
之前预约了安装家电的师傅,刚好也是周六。
所以只有女朋友一个人去家那边,边打扫卫生,边等着安装师傅。
安装师傅全部弄好之后也是 19 点之后了。
因为我从公司到家特别的近。女朋友觉得我也差不多该下班了,于是决定就在家里等我,然后一起从家里回到租住的小区。
结果一等就是 2 个多小时。
我下班之后,马上打车到小区。
下午没有吃饭,工作也比较劳累,坐在车上,一阵疲倦的感觉袭来。
但是在小区门口刷门禁卡的时候,我一抬头,门口写着:欢迎回家。
那一刻,我突然觉得好暖啊,甚至还有一丝丝的感动。
走在小区的路上,感觉一切都是这么的可爱。
因为这个家,真的是属于自己的家,用自己一手一脚挣出来的钱堆出来的。
此时此刻,家里还有一个人,开着灯,在等着我回家。
之前我从来没有这样的感觉过,这是一种非常神奇的感觉。
到家之后,由于家具还没有准备好,我看到女朋友在地上铺着一个泡沫垫子,坐在上面,靠在墙上,通过手机看着综艺。
她起来抱了抱我,说:你终于回来啦。今天的事可真是多。
我们一起站在空荡荡的客厅中间。
那一刻,家的含义,家的感觉,从来没有这么具体过。
最后说一句(求关注)
才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在留言区提出来,我对其加以修改。 感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。
我是 why,一个被代码耽误的文学创作者,不是大佬,但是喜欢分享,是一个又暖又有料的四川好男人。
欢迎关注我呀。