1. 概述

本文主要针对对网上关于HashMap在多线程环境下会形成循环链表的问题进行一次总结.

2. 敲黑板的点

  1. 只会在低于jdk1.8的版本中发生(1.6, 1.7会有, 再古老的版本我就不知道了)
  2. jdk1.7的HashMap的数据结构使用的是数组+链表, 不存在红黑树.
  3. 这种情况发生在集合扩容的时候

3. 为什么会出现循环链表的情况呢?(jdk1.7)

多线程环境下, 多个线程同时对集合进行扩容时会发生.

下面翻开jdk1.7的resize源码一探究竟.

// 扩容方法
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<K,V> e : table) { // 这里的while主要针对存在链表的情况
while(null != e) { // 获取下一个元素, 如果存在链表, next就不为null
Entry<K,V> next = e.next; // 是否需要重新hash
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
} // 获取新下标
int i = indexFor(e.hash, newCapacity); // 第一次while循环时, newTable[i]是null
// 第二次while循环时, newTable[i]是第一次循环时的元素
// 原先链表的顺序为: 1,3,5,7,9
// 正常情况下, 扩容完成之后, 链表中元素的顺序为: 9,7,5,3,1
e.next = newTable[i]; // 覆盖上次循环的值, 因为上次循环时的值已经被链接到e.next上了
newTable[i] = e; // 继续循环链表上的下一个元素
e = next;
}
}
}

形成循环链表的代码就在transfer方法的while循环中, 正是因为扩容之后链表中元素的会发生逆转, 所以会产生循环链表.

举例说明:

/**
* [ 1, 3, 5, 7, 9] // HashMap table
* 11
* 12
* 13
* 14
* 15
*/

可以发现, 集合中下标0处发生了hash冲突, 产生了链表: 1,11,12,13,14,15.

现有两个线程: 线程A和线程B, 线程A进入while循环时, 执行到Entry<K,V> next = e.next;时被挂起了, 这时该线程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方法

可以参考我的另一片博客HashMap源码分析

改进之后的方法不再进行链表的逆转, 而是保持原有链表的顺序, 如果在多线程环境下, 顶多会在链表后边多追加几个元素而已, 不会出现环的情况.

5. HashMap的线程安全问题

毫无疑问, HashMap没有进行一点线程安全的控制, 甚至连volatile关键字都没有, 其实也不需要, 如果HashMap加入了一些线程安全控制的代码, 那么在单线程时, 这些线程安全的代码无疑时影响性能的相关要素. 所以没必要.

既然没有线程安全代码, 那么HashMap在多线程环境中一定是线程不安全的. 最简单的就是多个线程put元素时, 获取得到的size时不一样的, 因为没有加volatile关键字, 可以这么理解.

6. 总结

  1. jdk1.7 和 jdk1.8的HashMap实现不一样, 优化了存储结构和hash算法
  2. put元素时产生环形链表的问题已在jdk1.8中解决了.

关于HashMap多线程下环形链表的总结的更多相关文章

  1. hashmap,hashtable,concurrenthashmap多线程下的比较(持续更新)

    1.hashMap 多线程下put会造成死循环,主要是扩容时transfer方法会造成死循环. http://blog.csdn.net/zhuqiuhui/article/details/51849 ...

  2. 【java基础 12】HashMap中是如何形成环形链表的?

    导读:经过前面的博客总结,可以知道的是,HashMap是有一个一维数组和一个链表组成,从而得知,在解决冲突问题时,hashmap选择的是链地址法.为什么HashMap会用一个数组这链表组成,当时给出的 ...

  3. 多线程下HashMap的死循环问题

    多线程下[HashMap]的问题: 1.多线程put操作后,get操作导致死循环.2.多线程put非NULL元素后,get操作得到NULL值.3.多线程put操作,导致元素丢失. 本次主要关注[Has ...

  4. 多线程下HashMap的死循环是如何产生的

    前言 HashMap不是线程安全的,如果需要在多线程环境中使用Map,那么我们可以使用ConcurrentHashmap. 1.举例说明: package com.test; import java. ...

  5. 【JAVA】HashMap的原理及多线程下死循环的原因

    再次翻到以前工作中遇到的一个问题,HashMap在多线程下会出现死循环的问题,以前只是知道会死循环,导致CPU100%把机器拖跨,今天来彻底看看 首先来看下,HashMap的原理:HashMap是一个 ...

  6. HashMap为什么在多线程下会让cpu100%

    首先HashMap并不是sun公司多线程提供的集合,很多时候我们的程序是一个主线程,用了hashmap并没有什么问题,但是在多线程下会出现问题. hashmap是一个哈希表,存储的数据结构也可以是一个 ...

  7. ASP.NET MVC Filters 4种默认过滤器的使用【附示例】 数据库常见死锁原因及处理 .NET源码中的链表 多线程下C#如何保证线程安全? .net实现支付宝在线支付 彻头彻尾理解单例模式与多线程 App.Config详解及读写操作 判断客户端是iOS还是Android,判断是不是在微信浏览器打开

    ASP.NET MVC Filters 4种默认过滤器的使用[附示例]   过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...

  8. java--HashMap多线程并发问题分析

    并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多 ...

  9. HashMap多线程并发问题分析

    转载: HashMap多线程并发问题分析 并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题. ...

随机推荐

  1. vs2015 编译google v8

    转自:http://blog.csdn.net/runningman2012/article/details/54692010 系统Win10 64位,vs2015 1. git 下载depot_to ...

  2. 浏览器兼容java小程序配置说明

    最近在使用的一个web应用系统是内嵌了java小程序,遇到了各种浏览器兼容性问题,现梳理如下: 1.通过以下链接检测当前电脑是否已经安装有java https://java.com/zh_CN/dow ...

  3. Hibernate SQL

    SQL查询: 5.2之后的版本: NativeQuery<Order> sqlQuery = session.createNativeQuery("select * from t ...

  4. 我推荐阅读的微信公众号-IT类

    微信,正深刻影响着我们的生活,每个使用微信的人,从微信这个窗口去了解这个世界. 微信公众号,微信生态圈是核心功能之一,每天都有大量的文章创作.传播.转发出来,海量的信息扑面而来,微信阅读成为微信使用者 ...

  5. python基础知识-(1)语法基础知识总结(转载)

    1.Python标识符 在 Python 里,标识符有字母.数字.下划线组成. 在 Python 中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. Python 中的标识符是区分大 ...

  6. 使用Mongo dump 将数据导入到hive

    概述:使用dump 方式将mongo数据导出,上传到hdfs,然后在hive中建立外部表. 1.     使用mongodump 将集合导出 mongodump --host=localhost:27 ...

  7. python 27 获取时区转换后的时间

    python3的datetime有timezone属性,这里介绍python2.7环境下,获取时区转换后的时间. 利用第三方插件,pytz,没有安装的话安装一下. #!/usr/bin/env pyt ...

  8. 2-scala集合

    1.集合的数据结构: array.seq .set .map(映射) 2.集合分类 集合分为可变集合和不可变集合(除元组外,元组没有可变和不可变) scala.collection.immutable ...

  9. 爬虫概念 requests模块

    requests模块 - 基于如下5点展开requests模块的学习 什么是requests模块 requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求.功能 ...

  10. Angularjs中的Controller

    概念:一个应用(APP,本身也是一个大模块)是由若干个模块(module)组成的,每个模块实现一个功能.利于代码的复用. 书写格式: <!DOCTYPE html> <html ng ...