解析为什么hashmap是线程不安全的?「建议收藏」
- 扩容
一般我们声明HashMap时,使用的都是默认的构造方法:HashMap<K,V>,看了代码你会发现,它还有其它的构造方法:
HashMap(int initialCapacity, float loadFactor),
其中参数initialCapacity为初始容量,loadFactor为加载因子,扩容就是在put加入元素的个数超过initialCapacity * loadFactor的时候就会将内部Entry数组大小扩大至原来的2倍,然后将数组元素按照新的数组大小重新计算索引,放在新的数组中,同时修改每个节点的链表关系(主要是next和节点在链表中的位置)。
假设这里有两个线程同时执行了put()操作,并进入了transfer()环节:
刚开始:
线程1中的e指向key(0),next指向key(4),此时线程1挂起。
线程2调度完成所有节点的移动,移动后结果为:
线程1继续执行,线程一会把线程二的新表当成原始的hash表,将原来e指向的key(0)节点当成是线程二中的key(0),放在自己所建table[0]的头节点。注意线程1的next仍然指向key(4),
虽然此时key(0)的next已经是null。
- 执行e.next = newTable[i],于是 key(0)的 next 指向了线程1的新 Hash 表,因为新 Hash 表为空,所以e.next = null,
- 执行newTable[i] = e,所以线程1的新 Hash 表第一个元素指向了线程2新 Hash 表的 key(0)。好了,e 处理完毕。
- 执行e = next,将 e 指向 next,所以新的 e 是 key(4)
线程1的e指向了上一次循环的next,也就是key(4),此时key(4)的next已经是key(0)。将key(4)插入到table[0]的头节点,并且将key(4)的next设置为key(0)。此时仍然没有问题。
- 现在的 e 节点是 key(4),首先执行Entry<K,V> next = e.next,那么 next 就是 key(0)了
- 执行e.next = newTable[i],于是key(0) 的 next 就成了 key(4)
- 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(4)
- 执行e = next,将 e 指向 next,所以新的 e 是 key(0)
- 现在的 e 节点是 key(0),首先执行Entry<K,V> next = e.next,那么 next 就是 null
- 执行e.next = newTable[i],于是key(0) 的 next 就成了 key(4)
- 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(0)
- 执行e = next,将 e 指向 next,所以新的 e 是 key(4)
总结版:HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183168.html原文链接:https://javaforall.cn
相关文章
- 高并发下线程安全的单例模式(最全最经典)
- SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的?「建议收藏」
- 同步锁-线程安全问题解决方案「建议收藏」
- libcopp的线程安全、栈池和merge boost.context 1.64.0
- synchronized+Spring事务,为啥还线程不安全呢?
- 线程安全的queue-浅谈线程安全那些事儿
- c 线程安全的单例模式-设计模式之单例模式(C++版)
- [译文]Go线程安全 - 一个被忽略的问题
- Linux线程锁:实现安全多线程(linux线程锁)
- Linux下安全删除某些文件的方法(linux删除部分文件)
- Linux下实现线程安全的技术思路(linux下线程安全)
- Python 线程安全(同步锁Lock)详解
- Java非线程安全问题的解决方法
- Linux系统如何安全关闭线程(linux线程关闭吗)
- 创建MySQL用户:简单又安全(创建mysql用户)
- 探秘Linux黑客系统:安全还是犯罪利器?(黑客linux系统)
- 安全使用MongoDB实现线程安全(mongodb线程)
- 铁匠总结Linux服务器基本安全配置手册
- MSSQL 密码截取:安全挑战与技术对抗(截取mssql密码)
- Linux授权用户组:为你的系统更安全开启新的模式(linux授权用户组)
- Linux安全管理:保护你的系统安全。(linux安全管理)
- MySQL线程安全:实现多线程并发操作的保障。(mysql 线程安全)
- Linux服务器安全之重置密码(linux服务器重置密码)
- 的保护Oracle中的安全防护保护数据不受破坏(oracle中数据)
- 重要数据以Redis部分备份实现安全存储(redis部分备份)
- 线程安全Redis实现线程安全的技术利用分布式锁(redis通过什么锁实现)
- 入门典籍之Linux用户安全
- 如何:对Windows窗体控件进行线程安全调用