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 为当前线程中的数据.我取的时候根据 ...
随机推荐
- java连接oracle数据库详细代码
详细代码: import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;imp ...
- 网站SEO优化中内部链接的优化
重要性:内链有效的优化能够间接的提高某页面的权重达到搜索排名靠前的效果.同时有效的带领搜索引擎蜘蛛对整站进行抓取. 网站头部导航: 这个导航称为'网站主导航',当用户来到网站需要给他们看到的内容.也就 ...
- Java程序员面试题集(51-70)(转)
转:http://blog.csdn.net/jackfrued/article/details/17403101 Java程序员面试题集(51-70) 摘要:这一部分主要讲解了异常.多线程.容器和I ...
- leetcode_question_73 Set Matrix Zeroes
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. Follow ...
- SQLServer查询逻辑读最高的语句
select top 25 p.name as [SP Name], deps.total_logical_reads as [TotalLogicalReads], deps.total_logic ...
- Base64编码和解码算法
Base64么新鲜的算法了.只是假设你没从事过页面开发(或者说动态页面开发.尤其是邮箱服务),你都不怎么了解过,仅仅是听起来非常熟悉. 对于黑客来说,Base64与MD5算法有着相同的位置.由于电子邮 ...
- C++实现20个设计模式
http://c.chinaitlab.com/special/sjms/Index.html 一个月下来,把常见的20个设计模式好好复习并且逐个用C++实现了一遍,收获还是很大的,很多东西看上去明白 ...
- Follow-up letter to information seeking meeting, e-mail version
Subject: (logical to recipient!) Thank you for meeting Tuesday, Nov. 23 November 26, 20XY Mr. Jame ...
- 整理js继承
实现继承的方法: 一,原型链:利用原型让一个引用类型继承另一个引用类型的属性和方法 function SuperType(){ this.property=true; } SuperType.prot ...
- Git 添加空文件夹的方法
转自stackoverflow: http://stackoverflow.com/questions/115983/how-do-i-add-an-empty-directory-to-a-git- ...