HashMap为什么不安全?
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
javadoc中关于hashmap的一段描述如下:
此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:
1. 在 Items[Size] 的位置存放此元素;
2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
有两个原因
存放数据时,HashMap不是线程安全的
比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
HashMap在容量不足的时候会进行resize,将自己扩容为原来的两倍,而reszie不是线程安全的
假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。 假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。
1.Map概述 我们都知道HashMap是线程不安全的,但是HashMap的使用频率在所有map中确实属于比较高的。因为它可以满足我们大多数的场景了。 Map类继承图 复制代码 Map是一个接口,我们常用的实现类...
2.HashMap的实现 java7和java8在实现HashMap上有所区别,当然java8的效率要更好一些,主要是java8的HashMap在java7的基础上增加了红黑树这种数据结构,使得在桶里面查找数据的...
为什么HashMap使用红黑树而不使用AVL树?
AVL树和红黑树有几点比较和区别:(1)AVL树是更加严格的平衡,因此可以提供更快的查找速度,一般读取查找密集型任务,适用AVL树。(2)红黑树更适合于插入修改密集型任务。(3)通常,AVL树的旋转比红黑树的旋转更加难以平衡和调试。总结:(1)AVL以及红黑树是高度平衡的树数据结构。它们非常相似,真正...
HashMap原理 之 hash为什么要右移16位异或?
其实是为了减少碰撞,进一步降低hash冲突的几率。int类型的数值是4个字节的,右移16位异或可以同时保留高16位于低16位的特征 首先将高16位无符号右移16位与低十六位做异或运算。如果不这样做,而是直接做&运算那么高十六位所代表的部分特征就可能被丢失 将高十六位无符号右移之后与低十六位做异或运算 ...
为什么hashmap输出的是@?
如果一个HashMap输出的值(如System.out.println(map);)是一个@符号,那说明HashMap的toString方法没有被正确地覆盖,输出的就是对象的默认toString方法。默认情况下,任何类继承自Object类,如果没有自己实现toString()方法,那就继承Object中的toString()方法,默认的实现就是返回类名加hashCode。在Hash...
hashmap多线程不安全的原因
我们知道hashmap在多线程下是不安全的,那么为什么不安全,这个原因是什么呢。其实核心原因在于扩容的时候多线程的参与会造成前后节点之间相互引用,造成链环,下面我们就分析下这个是怎么产生的。我们假设一个场景:hashmap里面就两个元素,里面其中索引1下面有两个元素:3和7,然后在扩容后为4个元素,...
HashMap为什么比数组查询速度快?
这个结论不是绝对的。数组查询按照顺序从0开始向后查询,HashMap是打乱了顺序,所以可能快一些,但不一定。
HashMap原理 — 扩容机制及存取原理
简单解释一下:我们关注一下这里面最重要的三个方法,hash(),putVal(),resize().1. hash方法 我们通过hash方法计算索引,得到数组中保存的位置,看一下源码 我们可以看到HashMap中的hash算法是通过key的hashcode值与其hashcode右移16位后得到的值进行异或运算得到的,那么为什么不直接使用key.hashCode()...
JDK8的HashMap为什么要引入红黑树?
当HashMap的key冲突过多时,会导致链表过长。而链表的查询效率很差,因此引入红黑树优化查询效率。为什么当链表长度大于8时候才会转红黑树而不是一开始直接使用红黑树:树节点占用空间是普通节点的两倍,因此在开始较短时候使用链表,占用空间少,查询性能也相差不大。但是当链表越来越长,查询效率逐渐变低...
hashmap 为什么是线程不安全
当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操纵,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丧失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有...
为什么Hashmap 1.8 扩容时计算节点新位置是否移动使用e.hash & old...
有些节点扩容后仍在原来的位置,有些节点是原始位置+扩容的大小值,计算是否需要加上扩容的大小值是根据节点的hash值位与原数组大小(e.hash & oldCap)来判断,如果该值等于0,则不需要移动,不等于0则需要移动,为什么可以用这种方式来计算,下面就逐步分析。1、hashmap计算节点在数组中的位置是使用 ...
...hashmap是属于异步的呢?并且异步的hashmap为什么适合单线程_百度知 ...
摘抄的,学到了 HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题。HashMap的工作原理、ArrayList与Vector的比较以及这个问题是有关Java 集合框架的最经典的问题。Hashtable是个过时的集合类,存在于Java API中很久了。在Java ...
达奚怎素定: HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点.对链表而言,新加入的节点会从头结点加入.javadoc中关于hashmap的一段描述如下:此实现不是同步的....
博兴县17375271859: 为什么hashmap线程不安全 - ?
达奚怎素定: 一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组...
博兴县17375271859: hashmap 为什么线程不安全 - ?
达奚怎素定: 有2种办法让HashMap线程安全,分别如下:方法一:通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的. 这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map的实现.方法二:重新改写了HashMap,具体的可以查看java.util.concurrent.ConcurrentHashMap. 这个方法比方法一有了很大的改进.
博兴县17375271859: 如何线程安全的使用HashMap - ?
达奚怎素定: 在周二面试时,一面的面试官有问到HashMap是否是线程安全的,如何在线程安全的前提下使用HashMap,其实也就是HashMap,Hashtable,ConcurrentHashMap和synchronized Map的原理和区别.当时有些紧张只是简单说了下HashMap不是线...
博兴县17375271859: Java中,map分为哪些种类? - ?
达奚怎素定: Map有:HashMap、TreeMap、Hashtable.1、HashMap:线程不安全,键、值不允许为null,并且没顺序.2、Hashtable:线程安全,键、值允许为null,并且没顺序.3、TreeMap:线程不安全、键、值不允许为null,并且没顺序. 备注:当前用的最多的是HashMap,因为线性不安全,也就是说读取和存储效率要更高一些.
博兴县17375271859: hashmap多线程为什么造成死循环 - ?
达奚怎素定: 一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题? HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头
博兴县17375271859: hashmap是线程安全还是不安全的 - ?
达奚怎素定: String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用.因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了.其他的wrapper类也有这个特点.不可变性是必要的,因为为了...
博兴县17375271859: javaAPI文档中写hashmap“此实现不是同步的”线程不安全而hashtable“此实现是同步的”线程安全?
达奚怎素定: 意思是hashmap在多个线程同时操作时,结果会不可预料.而hashtable不会出现这个问题.具体原理你可以多了解一下多线程的东西.
博兴县17375271859: Hashtable与HashMap有什么区别? - ?
达奚怎素定: hashmap 线程不安全 允许有null的键和值 效率高一点、 方法外同步 有containsvalue和containsKey方法 HashMap 是Java1.2 引进的Map interface 的一个实现 HashMap是Hashtable的轻量级实现 hashtable 线程安全 不允许有null的键和值 效率稍低、 方法是是Synchronize的 有contains方法方法 、Hashtable 继承于Dictionary 类 Hashtable 比HashMap 要旧
博兴县17375271859: java hashset为什么线程不安全 - ?
达奚怎素定: 安全和效率的zd问题 table虽然线程安全, 但是效率底 我们很多时候不会考虑到多线程的问题, 所以正确的做法就是如果需要专属, 使用效率高的hashmap而同时自己去同步, 而不是为了少写点编码, 而不管需要不需要同步都去使用效率低的hashtable