关于HashMap多线程下环形链表的总结
2021-06-29 11:04
标签:href val 情况下 控制 jdk tps 存储 循环 冲突 目录 本文主要针对对网上关于HashMap在多线程环境下会形成循环链表的问题进行一次总结. 在多线程环境下, 多个线程同时对集合进行扩容时会发生. 下面翻开jdk1.7的resize源码一探究竟. 形成循环链表的代码就在transfer方法的while循环中, 正是因为扩容之后链表中元素的会发生逆转, 所以会产生循环链表. 举例说明: 可以发现, 集合中下标0处发生了hash冲突, 产生了链表: 1,11,12,13,14,15. 现有两个线程: 线程A和线程B, 线程A进入while循环时, 执行到 可以参考我的另一片博客HashMap源码分析 改进之后的方法不再进行链表的逆转, 而是保持原有链表的顺序, 如果在多线程环境下, 顶多会在链表后边多追加几个元素而已, 不会出现环的情况. 毫无疑问, HashMap没有进行一点线程安全的控制, 甚至连volatile关键字都没有, 其实也不需要, 如果HashMap加入了一些线程安全控制的代码, 那么在单线程时, 这些线程安全的代码无疑时影响性能的相关要素. 所以没必要. 既然没有线程安全代码, 那么HashMap在多线程环境中一定是线程不安全的. 最简单的就是多个线程put元素时, 获取得到的size时不一样的, 因为没有加volatile关键字, 可以这么理解. 关于HashMap多线程下环形链表的总结 标签:href val 情况下 控制 jdk tps 存储 循环 冲突 原文地址:https://www.cnblogs.com/wuqinglong/p/9646498.html
1. 概述
2. 敲黑板的点
3. 为什么会出现循环链表的情况呢?(jdk1.7)
// 扩容方法
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
/**
* Transfers all entries from current table to newTable.
* 这段代码主要是遍历原集合中的所有Entry, 然后依次将他们放入到新的集合中.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
// 遍历所有Entry
for (Entry
/**
* [ 1, 3, 5, 7, 9] // HashMap table
* 11
* 12
* 13
* 14
* 15
*/
Entry
时被挂起了, 这时该线程A中的e=1, e.next=11. 此时线程B进入while循环, 这时线程B中的e=1, e.next=11, 然后线程B继续向下执行, 执行完第一次while循环之后, 链表的顺序就变为11,1,12,13,14,15. 这时元素11的下一个元素时1, 而此时线程A中元素1的下一个元素为11. 完美! 产生了循环链表!4. jdk1.8中改进了resize方法
5. HashMap的线程安全问题
6. 总结