HashMap为什么线程不安全(hash碰撞与扩容导致)
一直以来都知道HashMap是线程不安全的,但是到底为什么线程不安全,在多线程操作情况下什么时候线程不安全?
让我们先来了解一下HashMap的底层存储结构,HashMap底层是一个Entry数组,一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突,Entry内部的变量:
- final Object key;
- Object value;
- Entry next;
- int hash;
通过Entry内部的next变量可以知道使用的是链表,这时候我们可以知道,如果多个线程,在某一时刻同时操作HashMap并执行put操作,而有大于两个key的hash值相同,如图中a1、a2,这个时候需要解决碰撞冲突,而解决冲突的办法上面已经说过,对于链表的结构在这里不再赘述,暂且不讨论是从链表头部插入还是从尾部初入,这个时候两个线程如果恰好都取到了对应位置的头结点e1,而最终的结果可想而知,a1、a2两个数据中势必会有一个会丢失,如图所示:
再来看下put方法
- public Object put(Object obj, Object obj1)
- {
- if(table == EMPTY_TABLE)
- inflateTable(threshold);
- if(obj == null)
- return putForNullKey(obj1);
- int i = hash(obj);
- int j = indexFor(i, table.length);
- for(Entry entry = table[j]; entry != null; entry = entry.next)
- {
- Object obj2;
- if(entry.hash == i && ((obj2 = entry.key) == obj || obj.equals(obj2)))
- {
- Object obj3 = entry.value;
- entry.value = obj1;
- entry.recordAccess(this);
- return obj3;
- }
- }
- modCount++;
- addEntry(i, obj, obj1, j);
- return null;
- }
put方法不是同步的,同时调用了addEntry方法:
- void addEntry(int i, Object obj, Object obj1, int j)
- {
- if(size >= threshold && null != table[j])
- {
- resize(2 * table.length);
- i = null == obj ? 0 : hash(obj);
- j = indexFor(i, table.length);
- }
- createEntry(i, obj, obj1, j);
- }
addEntry方法依然不是同步的,所以导致了线程不安全出现伤处问题,其他类似操作不再说明,源码一看便知,下面主要说一下另一个非常重要的知识点,同样也是HashMap非线程安全的原因,我们知道在HashMap存在扩容的情况,对应的方法为HashMap中的resize方法:
- void resize(int i)
- {
- Entry aentry[] = table;
- int j = aentry.length;
- if(j == 1073741824)
- {
- threshold = 2147483647;
- return;
- } else
- {
- Entry aentry1[] = new Entry[i];
- transfer(aentry1, initHashSeedAsNeeded(i));
- table = aentry1;
- threshold = (int)Math.min((float)i * loadFactor, 1.073742E+009F);
- return;
- }
- }
可以看到扩容方法也不是同步的,通过代码我们知道在扩容过程中,会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。
当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。
HashMap为什么线程不安全(hash碰撞与扩容导致)的更多相关文章
- hashMap的线程不安全
hashMap是非线程安全的,表现在两种情况下: 1 扩容: t1线程对map进行扩容,此时t2线程来读取数据,原本要读取位置为2的元素,扩容后此元素位置未必是2,则出现读取错误数据. 2 hash碰 ...
- HashMap之Hash碰撞源码解析
转自:https://blog.csdn.net/luo_da/article/details/77507315 https://www.cnblogs.com/tongxuping/p/827619 ...
- HashMap之Hash碰撞冲突解决方案及未来改进
说明:参考网上的两篇文章做了简单的总结,以备后查(http://blogread.cn/it/article/7191?f=wb ,http://it.deepinmind.com/%E6%80%A ...
- 从头认识java-15.7 Map(4)-介绍HashMap的工作原理-hash碰撞(常常作为面试题)
这一章节我们来讨论一下hash碰撞. 1.什么是hash碰撞? 就是两个对象的key的hashcode是一样的,这个时候怎么get他的value呢? 答案是通过equals遍历table那个位置上面的 ...
- Java基础知识强化之集合框架笔记80:HashMap的线程不安全性的体现
1. HashMap 的线程不安全性的体现: 主要是下面两方面: (1)多线程环境下,多个线程同时resize()时候,容易产生死锁现象.即:resize死循环 (2)如果在使用迭代器的过程中有其他线 ...
- 面试官:小伙子,你给我说一下HashMap 为什么线程不安全?
前言:我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线程不安全主要体现在什么地方呢,本文将对该问题进行解密. 1.jdk1.7中的HashMap 在jdk1.8中对HashM ...
- HashMap 为什么线程不安全?
作者:developer http://cnblogs.com/developer_chan/p/10450908.html 我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线 ...
- 为什么说 HashMap 是非线程安全的?
我们在学习 HashMap 的时候,都知道 HashMap 是非线程安全的,同时我们知道 HashTable 是线程安全的,因为里面的方法使用了 synchronized 进行同步. 但是 HashM ...
- Java并发基础08. 造成HashMap非线程安全的原因
在前面我的一篇总结(6. 线程范围内共享数据)文章中提到,为了数据能在线程范围内使用,我用了 HashMap 来存储不同线程中的数据,key 为当前线程,value 为当前线程中的数据.我取的时候根据 ...
随机推荐
- .net项目中上传大图片失败
.net项目中有时用户提出要上传大图片,一张图片有可能十几兆,本来用的第三方的上传控件,有限制图片上传大小的设置,以前设置的是2M.按照用户的要求,以为直接将限制图片上传大小的设置改下就可以了,但是当 ...
- Unity扩展让枚举视图中变成多选框
如图: 定义属性描述特性(因为没有描述的数据,让绘制类去绘制所以为空) using UnityEngine; using System.Collections; public class EnumFl ...
- poj 3216 (最小路径覆盖)
题意:有n个地方,m个任务,每个任务给出地点,开始的时间和完成需要的时间,问最少派多少工人去可以完成所有的任务.给出任意两点直接到达需要的时间,-1代表不能到达. 思路:很明显的最小路径覆盖问题,刚开 ...
- WAS下获取包路径下所有类
最近做javaweb项目的混淆工作,用到proguard,该工具混淆.jar文件比较方便,故把所有项目代码和配置文件打成jar包, 生成的jar包经过proguard处理后,再次打包(解决progua ...
- [WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ignored (webxml attribute is missing from war task, or ignoreWebxml attribute is specified as 'true')
WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ignored (webxml attribu ...
- 将mysql服务加入到系统服务中 服务器配置 注销时不会关闭mysql
将mysql加入系统服务中: 或者在cmd中输入:mysql安装路径\mysql\bin\mydqld.exe --install mysql --defaults-file="mysql安 ...
- 伪元素::before和::after
有时候我们的页面里面有不少其他网站的名字,而且还要求网站名后面还要有网站的链接,类似这样:百度(http://www.baidu.com).这个时候如果网站多的话写起来就很麻烦了 <a href ...
- PL/SQL分页查询
create or replace procedure fenye(tabelname in varchar2,currentpage in number,pageSize in number,inW ...
- Mac系统杂项 (持续更新)
一.调整LaunchPad的图标显示列数和行数 .调整每一列显示图标数量.在我的电脑上(1366 * 768),每列7个个人觉得比较不错 defaults write com.apple.dock s ...
- Remove Duplicate Letters
316. Remove Duplicate Letters Total Accepted: 2367 Total Submissions: 12388 Difficulty: Medium Given ...