为什么HashMap是线程不安全的

总说 HashMap 是线程不安全的,不安全的,不安全的,那么到底为什么它是线程不安全的呢?要回答这个问题就要先来简单了解一下 HashMap 源码中的使用的存储结构(这里引用的是 Java 8 的源码,与7是不一样的)和它的扩容机制

HashMap 内部存储使用了一个 Node 数组(默认大小是16),而 Node 类包含一个类型为 Node 的 next 的变量,也就是相当于一个链表,所有根据 hash 值计算的 bucket 一样的 key 会存储到同一个链表里(即产生了冲突)。

HashMap的自动扩容机制

HashMap 内部的 Node 数组默认的大小是16,假设有100万个元素,那么最好的情况下每个 hash 桶里都有62500个元素,这时get(),put(),remove()等方法效率都会降低。为了解决这个问题,HashMap 提供了自动扩容机制,当元素个数达到数组大小 loadFactor 后会扩大数组的大小,在默认情况下,数组大小为16,loadFactor 为0.75,也就是说当 HashMap 中的元素超过16\0.75=12时,会把数组大小扩展为2*16=32,并且重新计算每个元素在新数组中的位置。

为什么线程不安全

个人觉得 HashMap 在并发时可能出现的问题主要是两方面,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个 put 的 key 发生了碰撞(根据 hash 值计算的 bucket 一样),那么根据 HashMap 的实现,这两个 key 会添加到数组的同一个位置,这样最终就会发生其中一个线程的 put 的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小* loadFactor ,这样就会发生多个线程同时对 Node 数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给 table,也就是说其他线程的都会丢失,并且各自线程 put 的数据也丢失。

《Java并发编程的艺术》一书中是这样说的:HashMap 在并发执行 put 操作时会引起死循环,导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。

死循环并不是发生在 put 操作时,而是发生在扩容时。

如何线程安全的使用HashMap

了解了 HashMap 为什么线程不安全,那现在看看如何线程安全的使用 HashMap。这个无非就是以下三种方式:

  • Hashtable

    HashTable的get/put方法都被synchronized关键字修饰,说明它们是方法级别阻塞的,它们占用共享资源锁,所以导致同时只能一个线程操作get或者put,而且get/put操作不能同时执行,所以这种同步的集合效率非常低,一般不建议使用这个集合。
  • ConcurrentHashMap

    private Map<String, Object> map = new ConcurrentHashMap<>();
    这个也是最推荐使用的线程安全的Map,也是实现方式最复杂的一个集合,每个版本的实现方式也不一样,在jdk8之前是使用分段加锁的一个方式,分成16个桶,每次只加锁其中一个桶,而在jdk8又加入了红黑树和CAS算法来实现。
  • Synchronized Map

    private Map<String, Object> map = Collections.synchronizedMap(new HashMap<String, Object>());
    这种是直接使用工具类里面的方法创建SynchronizedMap

https://www.cnblogs.com/yaowen/p/9634368.html

https://www.cnblogs.com/xwjBlog/p/9708198.html

map集合中哪些是线程安全的的更多相关文章

  1. map集合修改其中元素 去除Map集合中所有具有相同值的元素 Properties长久保存的流操作 两种用map记录单词或字母个数的方法

    package com.swift.lianxi; import java.util.HashMap; import java.util.Iterator; import java.util.Map; ...

  2. 过滤掉map集合中key或value为空的值

    package cn.com.utils; import org.apache.commons.lang3.StringUtils; import java.util.Collection; impo ...

  3. Map集合中value()方法与keySet()、entrySet()区别

    http://blog.csdn.net/liu826710/article/details/9001254 在Map集合中 values():方法是获取集合中的所有的值----没有键,没有对应关系, ...

  4. 键盘录入一个文件夹路径,统计该文件夹(包含子文件夹)中每种类型的文件及个数,注意:用文件类型(后缀名,不包含.(点),如:"java","txt")作为key, 用个数作为value,放入到map集合中,遍历map集合

    package cn.it.zuoye5; import java.io.File;import java.util.HashMap;import java.util.Iterator;import ...

  5. 根据key删除Map集合中的key-value映射

    一:在遍历Map时是不可以删除key-value映射的,如果根据key删除,如下: public static void main(String[] args) { Map<String,Obj ...

  6. map集合中value()、keySet()、entrySet()区别

    在Map集合中 values():方法是获取集合中的所有的值----没有键,没有对应关系, KeySet():将Map中所有的键存入到set集合中.因为set具备迭代器.所有可以迭代方式取出所有的键, ...

  7. Map集合中value()方法与keySet()、entrySet()区别 《转》

    在Map集合中 values():方法是获取集合中的所有的值----没有键,没有对应关系, KeySet(): 将Map中所有的键存入到set集合中.因为set具备迭代器.所有可以迭代方式取出所有的键 ...

  8. Java分享笔记:使用entrySet方法获取Map集合中的元素

    /*--------------------------------- 使用entrySet方法取出Map集合中的元素: ....该方法是将Map集合中key与value的关系存入到了Set集合中,这 ...

  9. Java分享笔记:使用keySet方法获取Map集合中的元素

    /*--------------------------- Map集合中利用keySet方法获取所有的元素值: ....keySet方法:将Map中的所有key值存入到Set集合中, ....利用Se ...

随机推荐

  1. django后台管理-admin

    0922自我总结 django后台管理-admin 一.模型注册 admin.py 注册方式一: #在对于注册的app中的admin文件中导入模型然后注册模型 admin.site.register( ...

  2. Python_生成随机验证码

    内置函数 chr() ord() 这两个内置函数是用来对十进制(十六进制也可以)与ASCii之间进行转换 chr() : 将十进制转换成ASCii对应字母或符号 t_1 = chr(99) t_2 = ...

  3. Hibernate HQL注入与防御(ctf实例)

    遇到一个hql注入ctf题    这里总结下java中Hibernate HQL的注入问题. 0x01 关于HQL注入 Hibernate是一种ORM框架,用来映射与tables相关的类定义(代码) ...

  4. Oracle报错注入总结

    0x00 前言 在oracle注入时候出现了数据库报错信息,可以优先选择报错注入,使用报错的方式将查询数据的结果带出到错误页面中. 使用报错注入需要使用类似 1=[报错语句],1>[报错语句], ...

  5. 2019关于phpstudy软件后门简单分析

    2019.9.20得知非官网的一些下载站中的phpstudy版本存在后门文件   说是官网下的就没有后门 20号出现的新闻 今天phpstudy官网21号又更新一波 不太好说这是什么操作哦 此地无银三 ...

  6. 如何巧妙地在基于 TCP Socket 的应用中实现用户注册功能?

    通常,在基于TCP的应用中(比如我开源的GGTalk即时通信系统),当TCP连接建立之后,第一个请求就是登录请求,只有登录成功以后,服务器才会允许客户端进行其它性质的业务请求.但是,注册用户这个功能比 ...

  7. java集合之linkedList链表基础

    LinkedList链表: List接口的链接列表实现.允许存储所有元素(包含null).使用频繁增删元素. linkedList方法: void addFirst(E e) 指定元素插入列表的开头 ...

  8. Unity3D 通过JSON查询天气

    一.天气查询API 获取天气信息,首先要找到提供天气数据的接口,我使用的是高德地图免费为我们提供的,网址为 https://lbs.amap.com/api/webservice/guide/api/ ...

  9. 罕见的coredump了

    最近,项目在越南版删档测试的时候,发生了罕见的coredump,简单记一点排查日志 目前的敏感词过滤是在C层做判定的,先后经过几个项目考验,模块算是比较稳定了.越南版有个需求,需要将敏感词里的空格去掉 ...

  10. 浅谈原理--hashCode方法

    我们时常会判断一个元素是否相等重复,可以用equals方法. 每增加一个元素,我们就可以通过equals方法判断集合中的每一个元素是否重复,但是如果集合中有10000个元素了,我们每添加一个元素的时候 ...