ConcurrentHashMap源码详解

news2024/9/22 17:32:22
本文已收录于专栏
《Java》

目录

  • 概念说明
  • 数据结构
  • 线程安全
    • HashMap示例
    • 运行结果
    • ConcurrentHashMap示例
    • 运行结果
  • 涉及技术
    • Synchronized
      • 概念
      • 特性
    • CAS(Compare And Swap)
      • 概念
      • 原理
      • 代码演示
        • 没有使用CAS的代码
        • 运行结果
        • 使用CAS的代码
        • 运行结果
  • 总结提升

概念说明

  ConcurrentHashMap是Java中的线程安全的哈希表实现,它允许多个线程同时读取和写入数据,并且支持高并发访问。下面是ConcurrentHashMap、HashMap和HashTable的区别的二维表:

数据结构

在这里插入图片描述

  ConcurrentHashMap和HashMap的数据结构是一样,由数组+链表+红黑树组成的。当向数组中出入的元素的hashcode都一样的情况下会形成链表结果,由于链表的时间复杂度是O(n),当链表过长的时候就会导致查询数据比较慢。所以当数组的长度为8的时候,链表结果就会转换成红黑树的结构,红黑树的时间复杂度是O(nlong)来提高查询数据的效率。以下是Map中涉及到的参数说明:

// 数组容量
private static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认长度
private static final int DEFAULT_CAPACITY = 16;

// 链表树化条件-是根据线程竞争情况和红黑树的操作成本进行设计的。
static final int TREEIFY_THRESHOLD = 8;
// 取消树化条件-为了避免过度的树化,防止内存占用过高。
static final int UNTREEIFY_THRESHOLD = 6;              //链表结构中,每个节点只需要存储指向下一个节点的指针,而不需要存储节点的值。因此,链表只需要存储节点的引用,占用较少的内存空间。树结构中每个节点需要存储节点的值以及指向子节点的指针。

线程安全

  通过使用HashMap和ConcurrentHashMap来对比一下,当在高并发的情况下是否会发生线程安全的问题

HashMap示例

public class HashMapUnsafeTest {
    public static void main(String[] args) throws InterruptedException {
        //演示HashMap
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                //向集合添加内容
                map.put(key, UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.println(map);
            }, "").start();
        }
    }
}

运行结果

在这里插入图片描述

ConcurrentHashMap示例

public class ConcurrentHashMapSafe {
    public static void main(String[] args) throws InterruptedException {

        //演示ConcurrentHashMap
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                //向集合添加内容
                map.put(key, UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.println(map);
            }, "").start();

        }
    }
}

运行结果

在这里插入图片描述

涉及技术

Synchronized

概念

  synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。即synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是同步,也称之为”同步锁“。它包括两种用法:synchronized 方法和 synchronized 块。

特性

   「可见性 」:是指一个线程对共享变量进行修改,另一个线程立即得到修改后的新值。
   「原子性 」:在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行。
   「有序性 」:程序执行的顺序按照代码的先后顺序执行。编译器为了优化性能,有时候会改变程序中语句的先后顺序。

  在使用多线程进行并发编程的时候,如果有多个线程来操作共享数据,很有可能共享数据的值会出现错乱,我们称之为线程安全问题。导致出现问题的原因有:可见性问题;原子性问题;有序性问题。这时候我们就可以通过使用synchronized 关键字来解决出现的问题

CAS(Compare And Swap)

概念

在CAS中,有这样三个值:
  V:要更新的变量(var)
  E:预期值(expected)
  N:新值(new)
比较并交换的过程如下:
  判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。
在这里插入图片描述

原理

  unsafe类——以下是类中涉及到的三个方法用来实现CAS效果的,这三个方法都是由native进行修饰的。具体的实现是由C++写的。
在这里插入图片描述
  三个方法传入的参数都是一样的,只不过根据传入的类型不同选择不同的方法,第一个参数是当前这个对象,第二个参数线程之间共享的变量,第三个参数是预期值,第四个参数想要修改的值。

代码演示

没有使用CAS的代码

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.CAS
 * @Author: Wuzilong
 * @Description: 没有使用CAS的实例
 * @CreateTime: 2023-07-29 09:44
 * @Version: 1.0
 */

public class NoCASDemo {

    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        //线程一
        Thread thread1= new Thread(() -> {
            for (int i=0; i<10000;i++){
                counter++;
            }
        });
        //线程二
        Thread thread2= new Thread(() -> {
            for (int i=0; i<10000;i++){
                counter++;
            }
        });
        //执行线程
        thread1.start();
        thread2.start();
        //等待执行完线程1和2
        thread1.join();
        thread2.join();

        System.out.println("查看counter的总数"+counter);
    }

}

运行结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  多次运行程序会发现counter的总数是不一样的,说明有的线程操作的是同一个数值,导致两次i++的结果是一样的。

使用CAS的代码

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.CAS
 * @Author: Wuzilong
 * @Description: 使用CAS的实例
 * @CreateTime: 2023-07-29 09:36
 * @Version: 1.0
 */

public class CASDemo {

    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        //线程一
        Thread thread1= new Thread(() -> {
            for (int i=0; i<10000;i++){
                increment();
            }
        });
        //线程二
        Thread thread2= new Thread(() -> {
            for (int i=0; i<10000;i++){
                increment();
            }
        });
        //执行线程
        thread1.start();
        thread2.start();
        //等待执行完线程1和2
        thread1.join();
        thread2.join();

        System.out.println("查看counter的总数"+counter.get());
    }

    public static void increment() {
        int currentValue;
        int newValue;
        do {
            //获取counter对象的value值
            currentValue = counter.get();
            //将counter对象的value值加1
            newValue = currentValue + 1;

        } while (!counter.compareAndSet(currentValue, newValue));
    }

}

运行结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  多次运行程序发现counter的总数都是20000,这就说明使用了CAS之后不会出现线程安全的问题,当共享变量与预期值不一致的时候就取消了当前线程的操作。

总结提升

  ConcurrentHashMap是一个线程安全的哈希表实现,它通过锁分段技术实现了高效的并发性能,支持高效的并发更新和弱一致性的迭代器。但需要注意的是,ConcurrentHashMap不支持存储null键和null值。在多线程环境下,使用ConcurrentHashMap可以提高并发性能,并且无需额外的同步措施。



在这里插入图片描述


🎯 此文章对你有用的话记得留言+点赞+收藏哦🎯

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

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

相关文章

vue3自定义dialog createApp setup语法组件使用element

目录 index.vue <template><el-dialogcenterv-model"isVisible"width"650px":title"title":append-to-body"true"><div id"dialogMap_container"></div><template #footer><span class&q…

7月31日每日两题

第一题:再解炸弹人 小哼最近爱上了“炸弹人”游戏。你还记得在小霸王游戏机上的炸弹人吗?用放置炸弹的方法来消灭敌人。需将画面上的敌人全部消灭后,并找到隐藏在墙里的暗门才能过关。 现在有一个特殊的关卡如下。你只有一枚炸弹,但是这枚炸弹威力超强(杀伤距离超长,可…

【LeetCode】143.重排链表

题目 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交…

Michael.W基于Foundry精读Openzeppelin第16期——SignedSafeMath.sol

Michael.W基于Foundry精读Openzeppelin第16期——SignedSafeMath.sol 0. 版本0.1 SignedSafeMath.sol 1. 目标合约2. 代码精读2.1 mul(int256 a, int256 b)2.2 div(int256 a, int256 b)2.3 sub(int256 a, int256 b)2.4 add(int256 a, int256 b) 0. 版本 [openzeppelin]&#x…

试试这三款音频转换格式软件,看看可不可以转换mp3?

你是不是不知道音频转换格式有什么用呢&#xff1f;为什么要音频转换呢&#xff1f; 其实音频转换格式的原因是&#xff1a; ①兼容性问题&#xff1a;不同的设备支持不同的音频格式&#xff0c;如果你想在不同设备之间共享音频文件的话&#xff0c;那么需要将文件转换另一种…

Android HTTP使用(详细版)

前言 在面试过程中,HTTP 被提问的概率还是比较高的。 小林我搜集了 5 大类 HTTP 面试常问的题目,同时这 5 大类题跟 HTTP 的发展和演变关联性是比较大的,通过问答 + 图解的形式由浅入深的方式帮助大家进一步的学习和理解 HTTP 协议。 HTTP 基本概念 Get 与 Post HTTP 特性…

Spring AOP 中的代理对象是怎么创建出来的?

文章目录 1. AOP 用法2. 原理分析2.1 doCreateBean2.2 postProcessAfterInitialization2.3 getAdvicesAndAdvisorsForBean2.3.1 findCandidateAdvisors2.3.2 findAdvisorsThatCanApply2.3.3 extendAdvisors 2.4 createProxy 今天和小伙伴们聊一聊 Spring AOP 中的代理对象是怎么…

将number输入框改写成只能输入数字的输入框

去掉 type”number” 末尾的箭头 场景代码 场景 在很多场景下&#xff0c;输入框是会被要求限制只能输入数字&#xff0c;不能输入文字或者符号的。通常我们会使用input事件 正则表达式 去实现这个功能&#xff0c;但今天提供另一种css的方法&#xff0c;将 type”number” 的…

面试总结-Redis篇章(九)——Redis主从复制、主从数据同步原理

Redis其他面试问题 主从复制单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离主节点主要进行客户端的写操作&#xff0c;从节点进行客户端的读操作&#xff0c;因为Redis一直都是读多写少&…

iTOP-RK3568开发板Windows 安装 RKTool 驱动

在烧写镜像之前首先需要安装 RKTool 驱动。 RKTool 驱动在网盘资料“iTOP-3568 开发板\01_【iTOP-RK3568 开发板】基础资料 \02_iTOP-RK3568 开发板烧写工具及驱动”路径下。 驱动如下图所示&#xff1a; 解压缩后&#xff0c;进入文件夹&#xff0c;如下图所示&#xff1a;…

品牌活动 | 阿里云云原生技术实践营:大模型+CloudOS,实现企业智能化

近日&#xff0c;由阿里云举办的“云原生技术实践营-应用和容器实践专场”在广州顺利开展。行云创新CEO马洪喜作为受邀嘉宾之一&#xff0c;参加了本次活动&#xff0c;分享了主题为“API大语言模型&#xff0c;以非侵入式实现企业业务智能化变革”的演讲&#xff0c;向参会者展…

【华秋干货铺】一文轻松搞定PCB叠层和阻抗设计

为了减少在高速信号传输过程中的反射现象&#xff0c;必须在信号源、接收端以及传输线上保持阻抗的匹配。单端信号线的具体阻抗取决于它的线宽尺寸以及与参考平面之间的相对位置。特定阻抗要求的差分对间的线宽/线距则取决于选择的PCB叠层结构。 由于最小线宽和最小线距是取决…

WRF替换GVF数据

WRF替换GVF数据 GVF(Green Vegetation Fraction) 是决定WRF模拟局地/区域气候研究的关键参数。研究表明GVF对模式模拟温度、湿度和潜热误差校正贡献率分别为62%&#xff0c;87%和92%。因此本文提供对WRF中的GVF进行替换的具体方法。 1.GVF获取   目前还没有现成的GVF产品可以…

Java代码连接RabbitMQ服务器

目录 1.添加依赖 2.生产者代码 3.消费者代码 4.效果 1.发送消息 2.消费消息 5.注意 1.添加依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.12.0</version></dependenc…

12、springboot自定义banner

springboot自定义banner ▲ 控制Banner信息是否显示及在哪里显示&#xff1a; 在application.properties这个配置文件里面进行以下的属性设置&#xff1a;spring.main.banner-mode 属性进行设置&#xff0c;该属性支持如下3个属性值&#xff1a;- console&#xff1a;在Spring…

JavaWeb+jsp+Tomcat的教务查询系统

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88134601?spm1001.2014.3001.5503 jsp/tomcat7.05/MySQL5.7或8版本/ssm框架/spring/ Web框架&#xff1a;SpringBoot/ORM框架&#xff1a;Mybatis/安全框架&#xff1a;Shiro/分页插件&am…

指针进阶详解---C语言

❤博主CSDN:啊苏要学习 ▶专栏分类&#xff1a;C语言◀ C语言的学习&#xff0c;是为我们今后学习其它语言打好基础&#xff0c;C生万物&#xff01; 开始我们的C语言之旅吧&#xff01;✈ 目录 前言&#xff1a; 一.字符指针 二.指针数组 三.数组指针 四.数组、指针参数 …

html富文本编辑器

接了个单子&#xff0c;需要添加一个文章模块&#xff0c;一看用到的技术这么老&#xff0c;人傻了&#xff0c;纯html css js 。 在普通页面中 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"…

word xls有用小技巧

不少office、代码编辑等软件&#xff0c;很简单高效小技巧。Word xlsx 某一行或列不动&#xff1a; 视图》冻结窗格》冻结首行 eclispe 全局搜索 CtrlH 制定变量、名称搜索 鼠标左键点中CtrlAltG

Promethus(普罗米修斯)监控系统

一、普罗米修斯概述 Prometheus(由go语言(golang)开发)是一套开源的监控&报警&时间序列数据库的组合。适合监控docker容器。因为kubernetes(俗称k8s)的流行带动了prometheus的发展。 Overview | Prometheus 二、时间序列数据 1、什么是序列数据 时间序列数据(TimeSer…