HashMap在什么场景下会由哪些内部方法导致线程不安全,至少给出一种场景
一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
javadoc中关于hashmap的一段描述如下:
此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
1、
- void addEntry(int hash, K key, V value, int bucketIndex) {
- Entry e = table[bucketIndex];
- table[bucketIndex] = new Entry(hash, key, value, e);
- if (size++ >= threshold)
- resize(2 * table.length);
- }
在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失
2、
- final Entry removeEntryForKey(Object key) {
- int hash = (key == null) ? 0 : hash(key.hashCode());
- int i = indexFor(hash, table.length);
- Entry prev = table[i];
- Entry e = prev;
- while (e != null) {
- Entry next = e.next;
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k)))) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return e;
- }
- prev = e;
- e = next;
- }
- return e;
- }
删除键值对的代码如上:
当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改
3、addEntry中当加入新的键值对后键值对总数量超过门限值的时候会调用一个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);
- table = newTable;
- threshold = (int)(newCapacity * loadFactor);
- }
这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。
当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。
HashMap在什么场景下会由哪些内部方法导致线程不安全,至少给出一种场景的更多相关文章
- Android智能手机中各种音频场景下的audio data path
上一篇文章(Android智能手机上的音频浅析)说本篇将详细讲解Android智能手机中各种音频场景下的音频数据流向,现在我们就开始.智能手机中音频的主要场景有音频播放.音频录制.语音通信等.不同场景 ...
- OC与Swift混编,三种场景的实现方式
多语言并存时期,混编成为一种必须的方式 ,在多场影中实现OC和Swift语言的并存原来是如此简单 第一种场景,App中实现混编 创建桥接文件*.h 新建一个桥接文件,New File 选择 Heade ...
- 高性能场景下,HashMap的优化使用建议
1. HashMap 在JDK 7 与 JDK8 下的差别 顺便理一下HashMap.get(Object key)的几个关键步骤,作为后面讨论的基础. 1.1 获取key的HashCode并二次加工 ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- HashMap在并发场景下踩过的坑
本文来自网易云社区 作者:张伟 关于HashMap在并发场景下的问题有很多人,很多公司遇到过!也很多人总结过,我们很多时候都认为这样都坑距离自己很远,自己一定不会掉入这样都坑.可是我们随时都有就遇到了 ...
- 并发场景下HashMap死循环导致CPU100%的问题
参考链接:并发场景下HashMap死循环导致CPU100%的问题
- java中HashMap在多线程环境下引起CPU100%的问题解决(转)
最近项目中出现了Tomcat占用CPU100%的情况,原以为是代码中出现死循环,后台使用jstack做了dump,发现是系统中不合理使用HashMap导致出现了死循环(注意不是死锁). 产生这个死循环 ...
- 亿级流量场景下,大型架构设计实现【2】---storm篇
承接之前的博:亿级流量场景下,大型缓存架构设计实现 续写本博客: ****************** start: 接下来,我们是要讲解商品详情页缓存架构,缓存预热和解决方案,缓存预热可能导致整个系 ...
- HBase指定大量列集合的场景下并发拉取数据时卡住的问题排查
最近遇到一例,HBase 指定大量列集合的场景下,并发拉取数据,应用卡住不响应的情形.记录一下. 问题背景 退款导出中,为了获取商品规格编码,需要从 HBase 表 T 里拉取对应的数据. T 对商品 ...
随机推荐
- oracle sqlplus执行sql语句字符集问题
因为业务需要,现将一些包含中文的insert语句导入到oracle数据库中,由于数据量比较大,通过pl/sql*plus导入时非常慢(实测1.5M的文件大概执行20分钟),现在oracle服务器sql ...
- 记 Win10 - Archlinux - Archlinux(Emergency) 三系统安装/配置注意事项
起因是正常使用的archlinux做滚动更新,结果貌似有一个盘块写坏了(?). 手上没有U盘,进入不了linux,不好做fsck.于是直接就直接用win10了. 取消Fast Boot 当晚进入lin ...
- 4.图片左轮播图(swiper)
一.html部分 二.js部分 三.源代码部分 <body> <div id="box"> <img src="imges/111.jpg& ...
- 99乘法表(js)
//九九乘法表 let i,j,str; for(i=1;i<=9;i++) { str = ""; for(j=1;j<=i;j++) { str = str+i+' ...
- elementUI使用实录
新项目开发用到了elementUI,但是对这个虽然会用,但是细枝末节的东西每次都需要看官方文档才能想起来怎么用,故,记之. 1.form表单 -- 表单验证 在防止用户犯错的前提下,尽可能让用户更早地 ...
- 【转】float与double的范围和精度
原文:http://blog.csdn.net/wuna66320/article/details/1691734 1 范围 float和double的范围是由指数的位数来决定的. float的指数位 ...
- (六)buildroot使用详解
为什么要使用buildroot? (文件系统搭建,强烈建议直接用buildroot,官网[http://buildroot.uclibc.org/]上有使用教程非常详细)文件系统通常要包含很多第三方软 ...
- php程序的生命周期
1.PHP的运行模式: PHP两种运行模式是WEB模式.CLI模式.无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行. 1.当我们在终端敲入php这个命令的时候,它使用的是CLI. 它就像 ...
- Alpha版本
这个作业属于哪个课程 [课程链接][ https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/] 这个作业要求在哪里 [作业要求][ ...
- 虚拟机vmware的连接方式以及IP端口,协议等概念
1.NAT虚拟机相当于小弟,宿主机相当于大哥,宿主机虚拟出一个网段供虚拟机上网用 2.Bridge桥接,虚拟机和宿主机相当于局域网中的两台机器 3.Host-Only虚拟机只和宿主机通信,无法上网 3 ...