HashMap在JDK7和JDK8是有了一些不同的,具体体现如下:

  • JDK7HashMap底层是数组+链表,而JDK8是数组+链表+红黑树
  • JDK7扩容采用头插法,而JDK8采用尾插法
  • JDK7的rehash是全部rehash,而JDK8是部分rehash。
  • JDK8对于key的hash值计算相比于JDK7来说有所优化。

JDK7 HashMap

JDK7HashMap在多线程环境下会出现死循环问题。

假如此时A、B线程同时对一个HashMap进行put操作,且HashMap刚号达到扩容条件需要进行扩容

那么这两个线程都会取对HahsMap进行扩容(JDK7HashMap扩容调用 resize()方法,而resize()方法中需要调用transfer()方法将旧数组元素全部rehash到新数组中去重点:这里在多线程环境下就会出现问题)

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);
}
void transfer(Entry[] newTable, boolean rehash) {int newCapacity = newTable.length;//对数组的每一条链表遍历rehashfor (Entry<K,V> e : table) {while(null != e) {//保留下一个节点Entry<K,V> next = e.next;if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);}//得到对应在新数组中的索引位置int i = indexFor(e.hash, newCapacity);//尾插法e.next = newTable[i];newTable[i] = e;e = next;}}
}

我们假设现在有一个链表 C——>D,且C、D扩容后计算的索引位置依然不变,那他么还在同一链表中

现在A线程进入到transfer方法拿到A和它的下一个节点B(Entry<K,V> next = e.next;)后,A线程被挂起,此时B线程正常走流程将A、Brehash到新的数组中,那么根据头插法在新的数组中是D——>C

B执行完之后,A线程继续去执行

因为A获取到了 e = C,next = D,所以C可以进行rehash,C进行完后拿到D,发现D.next = C,所以D也可以进行rehash,那么此时因为D——>C,此时会再拿到C,发现C.next = null,但C不是null,所以C再进行rehash,此时链表尾 C——> D ——>C,因为此时e = NULL,所以退出循环,此时出现死循环。C——>D——>C。

各位可以好好想想这些话或者自己在草稿纸上画一画再来看下面的图!

B正常执行完成

A继续执行

因为A获取到了 e = C,next = D,所以C可以进行rehash

C进行完后拿到e = D,发现D.next = C,所以D也可以进行rehash

那么此时因为D——>C,此时会再拿到C,发现C.next = null,但C不是null,所以C再进行rehash

此时e = NULL,所以退出循环,此时出现死循环。C——>D——>C。

JDK8 HashMap

JDK1.8会出现数据覆盖的情况

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}

第6行代码:假设两个线程A、B都在进行put操作,并且根据key计算出的hash值相同,那么得到得索引下标也相同,当线程A执行完第六行代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。

第38行代码++size不安全,还是线程A、B,这两个线程同时进行put操作时,假设当前HashMap的zise大小为10,当线程A执行到第38行代码时,从主内存中获得size的值为10后准备进行+1操作,但是由于时间片耗尽只好让出CPU,线程B快乐的拿到CPU还是从主内存中拿到size的值10进行+1操作,完成了put操作并将size=11写回主内存,然后线程A再次拿到CPU并继续执行(此时size的值仍为10),当执行完put操作后,还是将size=11写回内存,此时,线程A、B都执行了一次put操作,但是size的值只增加了1,所有说还是由于数据覆盖又导致了线程不安全。

面试官问我HashMap哪里不安全,我憋了半天憋出了内伤?相关推荐

  1. hashmap扩容_面试官问:HashMap在并发情况下为什么造成死循环?一脸懵

    这个问题是在面试时常问的几个问题,一般在问这个问题之前会问Hashmap和HashTable的区别?面试者一般会回答:hashtable是线程安全的,hashmap是线程不安全的. 那么面试官就会紧接 ...

  2. 面试官问你HashMap底层你用线程安全吊打他

    面试中,HashMap可以说是必问的,既然这样,我们应该怎么准备怎么回答呢,看看这篇文章,估计你会懂点东西. 先看看这两张图,是其内部的存储结构 说起HashMap,我们可以先从底层实现说起,Hash ...

  3. 字节跳动面试官问我看过哪些源码,然后就没有然后了

    最近,我的一位朋友在找工作,已经拿到了美团.快手等公司的Offer,准备选择其中一家入职了. 后来他又接到了字节跳动的电话,通知他去参加三面.从二面到三面之间隔了挺久的,他以为都没戏了,结果就收到了通 ...

  4. eureka自我保护时间_阿里面试官问我:到底知不知道什么是Eureka,这次,我没沉默...

    文章首发:阿里面试官问我:到底知不知道什么是Eureka,这次,我没沉默 什么是服务注册? 首先我们来了解下,服务注册.服务发现和服务注册中心的之间的关系. 举个形象的例子,三者之间的关系就好像是供货 ...

  5. 面试官问我:Redis 内存满了怎么办

    转载自 想不到!面试官问我:Redis 内存满了怎么办 Redis占用内存大小 Redis的内存淘汰 LRU算法 LRU在Redis中的实现 LFU算法 问题 Redis占用内存大小 我们知道Redi ...

  6. 【020期】面试官问:Java 遍历 Map 集合有几种方式?效率如何?

    >>号外:关注"Java精选"公众号,回复"2021面试题",领取免费资料!"Java精选面试题"小程序,3000+ 道面试题在 ...

  7. 已经成功拿到了几个offer的我来告诉你,Android面试官问的一些问题,看完这一篇就没有拿不到的offer

    前言 我是2020年毕业于中南大学的计算机学院的,大家可以叫我小吴,我嘞毕业之后在华为实习了差不多一年多,一直都从事着Android开发. 然后2021年的时候因为我自己的一些原因打算离职到外面看看, ...

  8. 面试阿里!妹子终面,阿里面试官问:有没有男朋友? 结果...

    点击"开发者技术前线",选择"星标????" 在看|星标|留言,  真爱 作者:  前线小熙  | 责编: 可可   来源 :开发者技术前线  刚好要到端午节前 ...

  9. 面试官问,你有什么问题需要问我的吗?

    提示:点上方 ↑"Java大数据修炼之道" 领取海量资源! 面试结束前,面试官大多都会问 "你还有什么问题问我吗?" 很多人以为这个问题只是走走过场,是面试结束 ...

最新文章

  1. XSLT教程 [转]
  2. java 中的finally你知多少_Java 处理异常 9 个最佳实践,你知道几个?
  3. 机房线路老化了会有怎么样的影响?又该如何处理?
  4. 在对人的管理上,项目经理应该做些什么?
  5. LeetCode之First Unique Character in a String
  6. struts过滤器和拦截器的区别
  7. ­«持续更新«计算机名词梳理(知识点概括,名词解析,简答题)
  8. 微信小程序图书管理系统
  9. 【C/C++练习题】斐波那契数列
  10. maven几个plugins
  11. 小程序前端view内容重叠问题
  12. MySQL数据库基础——强大的select之二,更加精彩的功能
  13. 人机测试计算机,人机测试
  14. 为什么游戏需要热更新?
  15. 通过matlab编程,对以下图像分别添加高斯噪声和椒盐噪声(参数自定),并使用理想低通滤波器、高斯低通滤波器和巴特沃斯低通滤波器进行去噪。
  16. 匈牙利算法原理与Python实现
  17. amdroid studio给备忘录添加背景音乐
  18. No module named ‘pip._internal.vcs.git
  19. d3js 画布 概念
  20. WIN8 64bit的系统下 使用Razer地狱狂蛇驱动v2.0汉化版会死机...

热门文章

  1. oracle odp arraybindcount 极限,極限挑戰—C#+ODP 100萬條數據導入Oracle數據庫僅用不到1秒...
  2. Java数据结构 双向链表
  3. 【内网安全】——CS操作指南(二)
  4. 猜数字游戏穷举法(迅雷水晶矿场中的游戏求解)
  5. 首席新媒体运营商学院创始人黎想:如何做好活动策划?只要3步!
  6. 微压压力传感器的灵敏度和线性度
  7. 论csdn博客抄袭与“转载”
  8. 海思3559平台移植二维码解码库zbar
  9. CNN中feature map、卷积核、卷积核个数、filter、channel的概念解释,以及CNN 学习过程中卷积核更新的理解
  10. 哈工大计算机自动化那个好就业6,从哈工大转专业人数看学科冷热,计算机很香,土木工程悲剧了...