当前位置: 首页 > news >正文

如何设计线程安全的 HashMap?

如何设计线程安全的 HashMap?
HashMap 线程不安全的体现:

  1. 多线程下扩容死循环:JDK1.7中的 HashMap 使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的出现,形成死循环。因此,JDK1.8使用尾插法插入元素,在扩容时会保持链表元素原本的顺序,不会出现环形链表的问题。
  2. 多线程的put可能导致元素的丢失:多线程同时执行 put 操作,如果计算出来的索引位置是相同的,那会造成前一个 key 被后一个 key 覆盖,从而导致元素的丢失。此问题在JDK 1.7和 JDK 1.8 中都存在。
  3. put和get并发时,可能导致get为null:线程1执行 put 时,因为元素个数超出 threshold 而导致 rehash,线程2此时执行get,有可能导致这个问题。此问题在JDK 1.7和 JDK 1.8 中都存在。
    针对问题1,JDK1.8 使用尾插法已经解决了,因此我们需要重点解决问题2和问题3。
    思路一:使用 Synchronized 来实现线程安全的,给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞等待需要的锁被释放。
    优点:实现简单。
    缺点:竞争激烈的多线程场景中性能会变的很差。
    思路二:使用 ConcurrentHashMap JDK1.7 的实现思路, 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
    优点:并发访问率比Synchronized更高,效率也更高。
    思路三:使用 ConcurrentHashMap JDK1.8 的实现思路,采用CAS + synchronized实现更加低粒度的锁。
    优点:将锁的级别控制在了更细粒度的哈希桶元素级别,也就是说只需要锁住这个链表头结点(红黑树的根节点),就不会影响其他的哈希桶元素的读写,大大提高了并发度。
    CAS 机制在 ConcurrentHashMap 的具体体现:
  4. 在初始化数组时,它会以 CAS 的方式修改初始化状态,避免多个线程同时进行初始化;
  5. 在执行 put 方法初始化头节点时,它会以 CAS 的方式将初始化好的头节点设置到指定槽的首位,避免多个线程同时设置头节点;
  6. 在数组扩容时,每个线程会以 CAS 方式修改任务序列号来争抢扩容任务,避免和其他线程产生冲突;
  7. 在执行 get 方法时,它会以 CAS 的方式获取头指定槽的头节点,避免其他线程同时对头节点做出修改。
http://www.lryc.cn/news/209574.html

相关文章:

  • rpc汇总
  • OpenCV学习(五)——图像基本操作(访问图像像素值、图像属性、感兴趣区域ROI和图像边框)
  • 指针仪表读数YOLOV8NANO
  • 10000字!图解机器学习特征工程
  • Java 官方提供了哪几种线程池,分别有什么特点?
  • DTI-ALPS处理笔记
  • LVS集群-NAT模式
  • 微服务技术导学
  • p5.js 开发点彩画派的绘画工具
  • Java工具库——Commons IO的50个常用方法
  • Git: 仓库clone和用户配置
  • 构建外卖小程序:技术要点和实际代码
  • ubuntu安装配置svn
  • 『Jmeter入门万字长文』 | 从环境搭建、脚本设计、执行步骤到生成监控报告完整过程
  • Unity C#中LuaTable、LuaArrayTable、LuaDictTable中数据的增删改查
  • Spring常见面试题
  • 通过Vue自带服务器实现Ajax请求跨域(vue-cli)
  • Vue2-计算属性的用法
  • SM3加密udf
  • ce从初阶到大牛(两台主机免密登录)
  • CS224W2.3——传统基于特征的方法(图层级特征)
  • 【CSS】包含块
  • [SpringCloud] Nacos 简介
  • TypeScript - 字符串的字面类型
  • CRM客户管理系统源码 带移动端APP+H5+小程序
  • Mac版好用的Git客户端 Fork 免激活
  • 有一个带头结点的单链表L,设计一个算法使其元素递增有序
  • JAVA将EEE MMM dd HH:mm:ss zzz yyyy日期格式化为yyyy-MM-dd HH:mm:ss形式
  • 【Qt】文件系统
  • PostgreSQL 基础知识