最后
对于很多Java工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
再分享一波我的Java面试真题+视频学习详解+技能进阶书籍
调整Hash表大小resize:
void resize(int newCapacity)
{
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
…
//创建一个新的Hash Table
Entry[] newTable = new Entry[newCapacity];
//将Old Hash Table上的数据迁移到New Hash Table上
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
当table[]数组容量较小,容易产生哈希碰撞,所以,Hash表的尺寸和容量非常的重要。
一般来说,Hash表这个容器当有数据要插入时,都会检查容量有没有超过设定的thredhold,如果超过,需要增大Hash表的尺寸,这个过程称为resize。
多个线程同时往HashMap添加新元素时,多次resize会有一定概率出现死循环,因为每次resize需要把旧的数据映射到新的哈希表,这一部分代码在HashMap#transfer()
方法,如下:
void transfer(Entry[] newTable)
{
Entry[] src = table;
int newCapacity = newTable.length;
//下面这段代码的意思是:
// 从OldTable里摘一个元素出来,然后放到NewTable中
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;//取出第一个元素
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
标红代码是导致多线程使用hashmap出现CUP使用率骤增,出现死循环,从而多个线程阻塞的罪魁祸首。
3、图解HashMap死循环:
正常的ReHash的过程(单线程):假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。
最上面的是old hash 表,其中的Hash表的size=2, 所以key = 3, 7, 5
,在mod 2以后都冲突在table[1]
这里了。接下来的三个步骤是Hash表 resize成4,然后所有的<key,value>
重新rehash的过程。
并发下的Rehash(多线程)
1)假设我们有两个线程。
do {
Entry<K,V> next = e.next; // <–假设线程一执行到这里就被调度挂起了,执行其他操作
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
而我们的线程二执行完成了。于是我们有下面的这个样子:
注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。在这里线程一变成了操作经过线程二操作后的HashMap。
2)线程一被调度回来执行。
-
先是执行
newTalbe[i] = e;
-
然后是
e = next
,导致了e指向了key(7)
, -
而下一次循环的
next = e.next
导致了next指向了key(3)
。
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。
这里介绍了在多线程下为什么HashMap会出现死循环,不过在真实的生产环境下,不会使用线程不安全的HashMap的。
作者:powerfuler
blog.csdn.net/dingjianmin/article/details/79780350
期往精选 点击标题可跳转
【085期】面试官问:Java 中 long 是不是原子操作?为什么?
【086期】面试官:Spring Boot 如何解决跨域,举例 3 种解决方案?
【087期】Spring Boot+Kafka+ELK 完成海量日志收集(超详细)
【088期】面试官问:MySQL 创建索引需要遵循哪些原则?
总结
对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)
Java面试精选题、架构实战文档
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!
助的朋友可以帮忙点赞分享支持一下小编~**
你的支持,我的动力;祝各位前程似锦,offer不断!