HashMap的rehash源代码
1 | public V put(K key, V value) |
检查容量是否超标
1 | void addEntry(int hash, K key, V value, int bucketIndex) |
新建一个更大尺寸的hash表,然后把数据从老的Hash表中迁移到新的Hash表中。
1 | void resize(int newCapacity) |
迁移的源代码,注意高亮处:
1 | void transfer(Entry[] newTable) |
好了,这个代码算是比较正常的。而且没有什么问题。
正常的ReHash的过程
- 我假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。
- 最上面的是old hash 表,其中的Hash表的size=2, 所以key = 3, 7, 5,在mod 2以后都冲突在table[1]这里了。
- 接下来的三个步骤是Hash表 resize成4,然后所有的
重新rehash的过程
并发下的Rehash
1)假设我们有两个线程。我用红色和浅蓝色标注了一下。
我们再回头看一下我们的 transfer代码中的这个细节:
1 | do { |
而我们的线程二执行完成了。于是我们有下面的这个样子。
注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。
2)线程一被调度回来执行。
- 先是执行 newTalbe[i] = e;
- 然后是e = next,导致了e指向了key(7),
- 而下一次循环的next = e.next导致了next指向了key
(3)一切安好。
线程一接着工作。把key(7)摘下来,放到newTable[i]的第一个,然后把e和next往下移。
4)环形链接出现。
e.next = newTable[i] 导致 key(3).next 指向了 key(7)
注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。
于是,当我们的线程一调用到,HashTable.get(11)时,悲剧就出现了——Infinite Loop。
本文参考:https://coolshell.cn/articles/9606.html
本文链接: https://linbei.top/HashMap%E6%AD%BB%E5%BE%AA%E7%8E%AF/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!