HashMap线程不安全|Hashtable|ConcurrentHashMap

news2024/11/26 10:26:24

文章目录

    • 常见集合线程安全性
    • HashMap为什么线程不安全?
      • 怎么保证HashMap线程安全
    • Hashtable
    • ConcurrentHashMap 引入细粒度锁
      • 代码中分析
      • 总结
    • 小结

常见集合线程安全性

ArrayList、LinkedList、TreeSet、HashSet、HashMap、TreeMap等都是线程不安全的。

HashTable是线程安全的。

HashMap为什么线程不安全?

来看个例子

public static void main(String[] args) {
       HashMap<String, Integer> map = new HashMap<>();

       // 创建两个线程同时向HashMap中添加1000个元素
       Thread thread1 = new Thread(new Runnable() {
           @Override
           public void run() {
               for (int i = 0; i < 1000; i++) {
                   map.put(String.valueOf(i), i);
               }
           }
       });

       Thread thread2 = new Thread(new Runnable() {
           @Override
           public void run() {
               for (int i = 1000; i < 2000; i++) {
                   map.put(String.valueOf(i), i);
               }
           }
       });

       // 启动线程
       thread1.start();
       thread2.start();

       try {
           // 等待两个线程执行完成
           thread1.join();
           thread2.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       // 输出 HashMap 的大小
       System.out.println("map集合大小: " + map.size());
   }
   //输出结果:map集合大小:1920(这个数字在变动)

在多线程环境下,如果多个线程同时HashMap 进行修改操作(例如添加、删除元素),可能会导致数据结构破坏,进而引发各种问题,比如丢失数据等。

怎么保证HashMap线程安全

第一
如何保证HashMap的线程安全呢?可能你们想到了synchronized ,确实,你可以通过在添加元素时使用 synchronized 来确保 HashMap 的线程安全性。这样做可以在多线程环境下保证对 HashMap 的操作是互斥的,从而避免了多个线程同时修改 HashMap 导致的线程不安全问题。

 Thread thread1 = new Thread(new Runnable() {
      @Override
      public void run() {
      	  //synchronized (map) 中的 map 是一个对象锁
      	  //它指定了在执行同步代码块时使用的锁对象
          synchronized (map) {
              for (int i = 0; i < 1000; i++) {
                  map.put(String.valueOf(i), i);
              }
          }
      }
  });

当一个线程进入同步代码块(即 synchronized (map) 所包围的部分)时,它会尝试获取 map 对象的锁。如果这个锁当前没有被其他线程占用,那么该线程将获得锁,并可以执行同步代码块中的操作如果该锁已经被其他线程占用,那么该线程将被阻塞,直到锁被释放。被锁住的对象将会在同步代码块执行完毕后自动释放。

第二
使用Collections.synchronizedMap() 包装:
Map<Integer, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
这样可以获得一个线程安全的 HashMap,但性能可能不如 ConcurrentHashMap。

第三:
使用 ConcurrentHashMap:
ConcurrentHashMap 是专门为高并发环境设计的,JDK 1.8它使用了 CAS + synchronized 来保证线程安全性,而且性能表现优秀。
Map<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();


Hashtable

Hashtable 使用的是全表加锁的方式来保证线程安全,也就是说,当一个线程要对 Hashtable 进行读写操作时,它会对整个 Hashtable 加锁,阻塞其他所有线程的访问
在这里插入图片描述
HashTable 替代品 ConcurrentHashMap ,ConcurrentHashMap 引入了细粒度锁和无锁读取等技术,大大提高了并发环境下的性能和扩展性


ConcurrentHashMap 引入细粒度锁

ConcurrentHashMap 之所以是线程安全的,主要是因为它在内部实现时采用了特殊的机制来确保多个线程同时访问和修改数据时不会发生冲突。

JDK 1.7 版本中的实现:
ConcurrentHashMap 在 JDK 1.7 中使用了分段锁(Segmentation)的结构,将整个哈希表分成了多个段(Segment),每个段有自己的锁。不同段之间的修改操作可以并发进行(提高了并发性能),只有在同一段内的操作才需要竞争锁。

JDK 1.8 版本中的优化:
JDK 1.8 对 ConcurrentHashMap 进行了重大优化,废弃了分段锁的设计,而是采用了更细粒度的锁分离技术。
在 JDK 1.8 中,ConcurrentHashMap 内部使用了基于 CAS(Compare and Swap 是一种原子操作,用于在多线程环境下实现对共享数据的安全更新。CAS 是一种乐观锁机制,可以避免使用传统的互斥锁,提高了并发性能。) 操作的 synchronized 关键字
同时,ConcurrentHashMap 在 JDK 1.8 中引入了红黑树作为链表的替代结构,当链表长度达到一定阈值时,会将链表转换为红黑树,以提高查找效率。这种优化又提高了 ConcurrentHashMap 的并发性能和吞吐量。

代码中分析

来看看put方法

    public static  void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("a",111);
    }

在 JDK8 中,ConcurrentHashMap 的实现方式已经改变,不再采用分段锁的方式,而是采用了 CAS+Synchronized 的方式来保证线程安全

我们需要先了解tabAt ,casTabAt(本质是CAS 算法,看下面源码可知)的利用来保障线程安全的操作:
tabAt 方法用于从哈希表数组 tab 中获取指定索引 i 处的节点数组。
casTabAt 方法用于原子性地将哈希表数组 tab 中指定索引 i 处的值从 c 更新为 v。(CAS 比较并交换,可实现原子操作)

put源码分析:

  1. 遍历桶(Bin)时不加锁
    首先,代码会根据 hash 值找到对应的桶(tab[i]),并且如果该桶是空的(f == null),会尝试通过 CAS(compare-and-swap) 操作将新的节点放入其中,这一部分操作是无锁的。

    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
            break;
    }
    
  2. 对非空桶的头节点加锁
    当桶 tab[i] 已经存在节点时,会进入 synchronized (f) 代码块,其中 f 是桶中的头节点。通过对头节点 f 加锁,保证对该桶中的修改是线程安全的。

    synchronized (f) {
        if (tabAt(tab, i) == f) {
            // 锁定成功后,进行进一步操作
        }
    }
    
    • 这种加锁方式是“桶级别”的锁,而不是对整个 HashMapConcurrentHashMap 加全局锁,因此不会影响其他线程对其他桶的访问。

在这里插入图片描述

  1. 对链表或树结构的操作
    加锁后,代码会对链表中的节点进行遍历,或者在红黑树中进行查找(如果桶已经转化为树形结构)。在这个过程中,同步块只对桶的头节点加锁,而不是对每个节点加锁。因此,在修改链表或树的过程中,保证了线程安全性,但并不会影响其他线程对其他桶的操作。

    • 如果该桶是链表结构,会遍历链表并进行元素的插入操作:

      for (Node<K,V> e = f;; ++binCount) {
          K ek;
          if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
              oldVal = e.val;
              if (!onlyIfAbsent)
                  e.val = value;
              break;
          }
          Node<K,V> pred = e;
          if ((e = e.next) == null) {
              pred.next = new Node<K,V>(hash, key, value, null);
              break;
          }
      }
      
    • 如果该桶是树形结构(红黑树),则调用 putTreeVal 方法进行树中的插入操作:

      if (f instanceof TreeBin) {
          Node<K,V> p;
          binCount = 2;
          if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
              oldVal = p.val;
              if (!onlyIfAbsent)
                  p.val = value;
          }
      }
      
  2. 加锁粒度
    由于只对每个桶的头节点加锁,因此即使多个线程同时对不同的桶进行操作,也不会相互阻塞。只有在同一个桶中有多个线程试图修改数据时,才会发生锁竞争。这种设计提高了并发性能,相比全局锁的方式有更高的并发吞吐量。

  3. 树化操作
    当链表中的节点数量超过 TREEIFY_THRESHOLD 时,会将链表转化为红黑树以提高查询和修改效率。树化也是在线程安全的环境下进行的,但依然是通过对头节点加锁实现的。

    if (binCount >= TREEIFY_THRESHOLD)
        treeifyBin(tab, i);
    

总结

  • 加锁机制:对每个桶(tab[i])的头节点进行加锁。这种锁定策略使得同一个桶中的修改是线程安全的。
  • 并发性能优化:这种设计避免了全局锁,提高了在高并发环境下的性能。当多个线程操作不同的桶时,操作不会相互干扰。

小结

而在 JDK 1.8 中,ConcurrentHashMap 放弃了分段锁,而是采用了更为精细的桶结构。每个桶可以独立加锁,使得并发修改操作可以更细粒度地进行。此外,当桶中的元素数量达到一定阈值时,链表结构会转变为红黑树,以减少搜索时间。这种锁分离技术提高了并发性能,使得 ConcurrentHashMap 在并发情况下表现更加出色。它是通过 CAS + synchronized 来实现线程安全的,并且它的锁粒度更小,查询性能也更高。


❤觉得有用的可以留个关注ya❤

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2128765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

50ETF期权可以当天买卖吗?

今天期权懂带你了解50ETF期权可以当天买卖吗&#xff1f;期权日内交易指的是在一个交易日内买入和卖出期权合约&#xff0c;以从价格波动中获取短期收益。 50ETF期权可以当天交易 在这里&#xff0c;可以负责任的告诉大家&#xff0c;50ETF期权当天买入&#xff0c;也可以当天…

安泰电压放大器对参数的要求有哪些

电压放大器是电子电路中常见的一种基本电路&#xff0c;用于将输入信号的电压放大至更大的幅值输出。在设计和应用电压放大器时&#xff0c;需要考虑一系列参数来确保放大器的性能和稳定性。下面将详细介绍电压放大器对参数的要求。 电压放大器对增益的要求非常重要。增益是指输…

2024年6款强力电脑监控软件推荐

随着数字化办公的普及&#xff0c;电脑监控软件已成为企业管理和个人安全的重要工具。无论是企业主监控员工工作效率&#xff0c;还是家长防范孩子沉迷网络&#xff0c;强力的电脑监控软件都能提供有效的解决方案。本文将为您推荐6款功能强大且广受好评的电脑监控软件&#xff…

UDS 诊断 - RequestDownload(请求下载)(0x34)服务

UDS 诊断服务系列文章目录 诊断和通信管理功能单元 UDS 诊断 - DiagnosticSessionControl&#xff08;诊断会话控制&#xff09;&#xff08;0x10&#xff09;服务 UDS 诊断 - ECUReset&#xff08;ECU重置&#xff09;&#xff08;0x11&#xff09;服务 UDS 诊断 - SecurityA…

【Hot100算法刷题集】双指针-02-盛水最多的容器(含暴力枚举、双指针法及其合理性证明)

&#x1f3e0;关于专栏&#xff1a;专栏用于记录LeetCode中Hot100专题的所有题目 &#x1f3af;每日努力一点点&#xff0c;技术变化看得见 题目转载 题目描述 &#x1f512;link->题目跳转链接 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的…

Linux系统部署SmartKG(知识图谱安装)

基本要求 #docker需要高版本 Docker version 20.10.14, build a224086docker 20.10.14离线安装 SmartKG官网 官方详细文档 下载部署包 SmartKG官网 准备部署 #上传到服务器 [roottest-server01 opt]# ll SmartKG-master.zip -rw-r--r-- 1 root root 79708691 Sep 11 17:4…

网络层协议介绍

目录 一、网络层的功能 二、ip数据包格式 三、ICMP协议&#xff08;Internet控制报文协议&#xff09; 3.1功能 3.2 ping命令 3.2.1ping命令的用法 3.2.2扩展 3.3 tracert命令&#xff08;windows&#xff09; 四、arp协议 4.1ARP协议是如何工作的 4.2工作原理&#x…

解码未来:H.265与H.266技术对比及EasyCVR视频汇聚平台编码技术优势

随着视频技术的不断发展&#xff0c;视频编码标准也在不断更新迭代。H.265&#xff08;也称为HEVC&#xff0c;High Efficiency Video Coding&#xff09;和H.266&#xff08;也称为VVC&#xff0c;Versatile Video Coding&#xff09;作为当前和未来的主流视频编码标准&#x…

本地部署大语言模型详细讲解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 本地部署大语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;需要相应的硬件资源和技术栈支持&#xff0c;如GPU计算能力、大量内存、存储空间&#xff0c;以及模型部署框架。以下是如…

46页PPT说清楚数据资产目录建设,数据资产目录建设方案

数据资产管理的必要性 为了更好地管理和利用这些资产&#xff0c;构建一个全面的数据资产目录变得至关重要。本方案旨在通过一系列策略和步骤&#xff0c;帮助企业实现数据资产的有效管理和利用。政策层面&#xff0c;数据已被纳入生产要素&#xff0c;强调了数据安全与保护的…

Qt 中openMp 配置

Qt 中openMp 配置 前言 openMP是使用CPU进行并行计算&#xff0c;在实际项目中处理耗时的for循环等&#xff0c;简单有效&#xff0c;本文记录下使用Qt \C项目开发时如何配置openMP 二、工程配置 1.Qt Creator pro工程 区分编译器&#xff0c;配置pro文件 MSVXXX编译器&am…

Adobe Firefly 视频模型测试版即将推出,具备文本、图像或视频添加元素的多种功能

Adobe Firefly Video 是一款由 Adobe 推出的新型人工智能视频生成模型&#xff0c;旨在为视频专业人士提供强大的创意工具&#xff0c;该模型计划在年底前推出。 演示视频在喜好儿网 Firefly Video 模型具备多种功能&#xff0c;包括从文本生成视频、图像生成视频或视频添加元…

I2C-Tools的安装与使用方法(详解,一篇教会你熟练使用)

1.前言&#xff1a; i2c-tools是一个开源的I2C总线工具集&#xff0c;它包括了用于访问和操作I2C设备的各种命令行工具。这些工具可以帮助用户读取和写入I2C设备的数据&#xff0c;检测和诊断I2C总线的问题等。 2.各系统的安装&#xff1a; 接下里会展示在不同操作系统中如何使…

00 目前大模型介绍

1 大模型种类 目前 主流的开源模型体系 分三种&#xff1a; 第一种&#xff1a; prefix Decoder 系 代表模型&#xff1a; ChatGLM 、ChatGLM2&#xff0c;第二种&#xff1a; causal Decoder 系 &#xff1a; LLaMA-7B 、 LLaMa 衍生物、Bloom,第三种&#xff1a;Encoder-De…

企业ESG表现、制造业的数字化转型及其高质量发展(2011-2022年)

在当今全球经济一体化的背景下&#xff0c;企业不仅要追求经济效益的最大化&#xff0c;还要关注环境、社会和治理&#xff08;ESG&#xff09;的表现&#xff0c;以及通过数字化转型实现高质量的发展。本文将基于2011-2022年的数据&#xff0c;探讨企业ESG表现与制造业数字化转…

pcs升压变流一体机

在当今全球积极推进能源转型的大背景下&#xff0c;新能源技术的发展日新月异。其中&#xff0c;PCS 升压变流一体机作为一种关键的能源转换设备&#xff0c;正发挥着越来越重要的作用。它一般可分为10KV与35KV等级的。 PCS 升压变流一体机&#xff0c;全称为 Power Conversion…

外卖跑腿APP开发详解:基于同城O2O系统源码的实现路径

近年来&#xff0c;随着O2O&#xff08;OnlinetoOffline&#xff09;模式的普及和发展&#xff0c;外卖跑腿服务已经成为了生活中不可或缺的一部分。外卖跑腿APP通过将用户、商家和骑手三方紧密连接&#xff0c;解决了用户日常所需的各种服务需求&#xff0c;为企业提供了新的商…

web基础之RCE

简介&#xff1a;RCE称为远程代码执行漏洞&#xff1b;是互联网的一种安全漏洞&#xff1b;攻击者可以直接向后台服务器远程注入操作系统命令&#xff1b;从而操控后台系统&#xff1b;也是CTF比较常考的一个方面 1、eval执行 &#xff08;1&#xff09;分析后端代码&#xf…

价值共创撬动产业增量,用友交出一份共赢的“生态答卷”

前言 在企业服务赛道&#xff0c;什么样的生态是产业更需要&#xff0c;伙伴更欢迎的&#xff1f; 看营收&#xff0c;伙伴数量还是伙伴收益&#xff1f; 用友的答案是&#xff1a;客户价值。 用客户价值这把标尺重新审视&#xff0c;企业服务生态值得重塑一遍。 尽管相比…

排班系统|基于Springboot+vue的医护人员排班系统(源码+数据库+文档)

排班系统|医护人员排班系统 目录 基于Springbootvue的医护人员排班系统 一、前言 二、系统设计 三、系统功能设计 医护类型管理 排班类型管理 科室信息管理 医护信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…