为什么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
  • ConcurrentHashMap
  • Synchronized Map

ConcurrentHashMap

ConcurrentHashMap (以下简称CHM)是 JUC 包中的一个类,Spring 的源码中有很多使用 CHM 的地方。之前已经翻译过一篇关于 ConcurrentHashMap 的博客,如何在java中使用ConcurrentHashMap,里面介绍了 CHM 在 Java 中的实现,CHM 的一些重要特性和什么情况下应该使用 CHM。需要注意的是,上面博客是基于 Java 7 的,和8有区别,在8中 CHM 摒弃了 Segment(锁段)的概念,而是启用了一种全新的方式实现,利用 CAS 算法,有时间会重新总结一下。

map的线程安全问题的更多相关文章

  1. Map容器线程安全问题

    一.HashMap在非线程安全的环境下使用会出现什么样的问题? public class HashMapMultiThread { static Map<String,String> ma ...

  2. 被我们忽略的HttpSession线程安全问题

    1. 背景 最近在读<Java concurrency in practice>(Java并发实战),其中1.4节提到了Java web的线程安全问题时有如下一段话: Servlets a ...

  3. 【GoLang】GoLang map 非线程安全 & 并发度写优化

    Catena (时序存储引擎)中有一个函数的实现备受争议,它从 map 中根据指定的 name 获取一个 metricSource.每一次插入操作都会至少调用一次这个函数,现实场景中该函数调用更是频繁 ...

  4. SimpleDateFormat 的线程安全问题与解决方式

    SimpleDateFormat 的线程安全问题 SimpleDateFormat 是一个以国别敏感的方式格式化和分析数据的详细类. 它同意格式化 (date -> text).语法分析 (te ...

  5. Java设计模式-单例模式及线程安全问题

    单例模式是非常常用的设计模式,他确保了一个类只有一个对象,并且这个对象是自己创建的,外界可以获取使用到这个对象. 单例模式一般有两种:懒汉式,饿汉式(其实还有一种登记式,把创建的对象放在map集合中, ...

  6. Spring中构造器、init-method、@PostConstruct、afterPropertiesSet孰先孰后,自动注入发生时间以及单例多例的区别、SSH线程安全问题

    首先明白,spring的IOC功能需要是利用反射原理,反射获取类的无参构造方法创建对象,如果一个类没有无参的构造方法spring是不会创建对象的.在这里需要提醒一下,如果我们在class中没有显示的声 ...

  7. 关于 SimpleDateFormat 的非线程安全问题及其解决方案

    一直以来都是直接用SimpleDateFormat开发的,没想着考虑线程安全的问题,特记录下来(摘抄的): 1.问题: 先来看一段可能引起错误的代码: package test.date; impor ...

  8. java线程安全问题原因及解决办法

    1.为什么会出现线程安全问题 计算机系统资源分配的单位为进程,同一个进程中允许多个线程并发执行,并且多个线程会共享进程范围内的资源:例如内存地址.当多个线程并发访问同一个内存地址并且内存地址保存的值是 ...

  9. JUC之集合中的线程安全问题

    集合线程安全问题 JDK Version:9 首先说下集合线程安全是什么:当多个线程对同一个集合进行添加和查询的时候,出现异常错误. 复现例子: package com.JUC; import jav ...

随机推荐

  1. X-扫描线算法

    多边形的扫描转换 多边形有两种重要的表示方法:顶点表示和点阵表示 顶点表示是用多边形的顶点序列来表示多边形.这种表示直观.几何意义强.占内存少,易于进行几何变换. 但由于它没有明确指出哪些象素在多边形 ...

  2. 第二章Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...

  3. Vue-CLI项目路由案例汇总

    0901自我总结 Vue-CLI项目路由案例汇总 router.js import Vue from 'vue' import Router from 'vue-router' import Cour ...

  4. django html母版

    08.12自我总结 django母版 一.母版写的格式 在需要导入的地方写 {% block 名字定义 %} {% endblock %} 二.导入模板 {% extends 'FUCK.html' ...

  5. Redis Getshell总结

    Redis默认端口是6379,可以通过zoomeye或者shodan来查找开放6379的端口,能收到很多对外开放6379端口的ip,而且绝大部门是未授权访问的,因为默认redis是未授权访问.但是还有 ...

  6. Ubuntu php安装xdebug

    1.安装xdebug扩展: sudo apt-get install php-xdebug 2.找到扩展的路径: 3.编辑php.ini文件,末尾加入,保存退出: [xdebug] zend_exte ...

  7. window 下Notepad++设置为文本文件的默认打开程序失败

    1.右键Notepad++的可执行程序,选择"属性"  -- "兼容性" , 设置Notepad++以管理员的身份运行 2.打开Notepad++ ," ...

  8. vue在一个方法执行完后再执行另一个方法

    vue在一个方法执行完后执行另一个方法 用Promise来实现.Promise是ES6的新特性,用于处理异步操作逻辑,用过给Promise添加then和catch函数,处理成功和失败的情况 ES7中新 ...

  9. opencv::处理边缘

    卷积边界问题 图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理. 处理边缘 ...

  10. mysql::批量入库

    批量入库 INSERT INTO M_Signal (Signal_Id, Signal_Name) VALUES(,,'water') , , , , 'water') ON DUPLICATE K ...