zoukankan      html  css  js  c++  java
  • map集合中哪些是线程安全的

    为什么HashMap是线程不安全的

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

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

    img

    HashMap的自动扩容机制

    HashMap 内部的 Node 数组默认的大小是16,假设有100万个元素,那么最好的情况下每个 hash 桶里都有62500个元素,这时get(),put(),remove()等方法效率都会降低。为了解决这个问题,HashMap 提供了自动扩容机制,当元素个数达到数组大小 loadFactor 后会扩大数组的大小,在默认情况下,数组大小为16,loadFactor 为0.75,也就是说当 HashMap 中的元素超过16.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

  • 相关阅读:
    在Postgre中设置max_connections时,为什么需要使用连接池 (译)
    既然python的多线程是"伪多线程",那么多线程访问共享资源的时候,还需要线程锁吗
    Debugging: MISCONF Redis is configured to save RDB snapshots(译)
    MySQL集群故障转移的时候在基于python的客户端连接层自动failover实现
    exec: "com.docker.cli": executable file not found in %PATH%
    windows 快捷操作系列
    Docker Build时查看当前镜像目录树
    vs2019生成的dockerfile 手动 build
    Python Records库使用举例
    Jupyter Notebook安装扩充插件与常见问题
  • 原文地址:https://www.cnblogs.com/PoetryAndYou/p/11615705.html
Copyright © 2011-2022 走看看