Java并发编程 - 常用容器(ConcurrentHashMap, ConcurrentLinkedQueue)

并发容器的原理,7大并发容器详解、及使用场景 并发容器的原理,7大并发容器详解、及使用场景 http://youzhixueyuan.com/use-of-concurrent-containers.html 并发容器的由来 在Java并发编程中,经常听到Java集合类,同步容器、并发容器,那么他们有哪些具体分类,以及各自之间的区别和优劣呢? 只有把这些梳理清楚了,你才能真正掌握在高并发的环境下,正确使用好并发容器,我们先从Java集... 阅读详情

ConcurrentHashMap的实现原理与使用

传统的HashMap不是线程安全的, 所以多线程进行put()和get()操作的时候可能会引发问题.
还有一个叫做HashTable的数据结构, 它使用的是synchronized来保证线程安全, 但是效率很低, 因为不能并发读.

ConcurrentHashMap采用的是锁分段技术, 将数据分成一段一段存储, 然后给每一段数据配一把锁,
当一个线程对某段数据进行写操作的时候, 其他段的数据也能被其他线程安全访问(读写).

ConcurrentHashMap的结构

  • 一个ConcurrentHashMap包含一个Segment数组;
  • 一个Segment里面包含一个HashEntry链表.

Segment是一种可重入锁, 在ConcurrentHashMap中扮演锁的角色.
HashEntry则是实际存储键值对数据的地方. 当对HashEntry的数据进行修改时, 必须首先获得与它对应的Segment锁.

ConcurrentHashMap

ConcurrentHashMap的初始化

初始化segments数组

if (concurrencyLevel > MAX_SEGMENTS) { // 如果自定义的concurrencyLevel超过允许最大的并发数
    concurrencyLevel = MAX_SEGMENTS; // 设定为允许的最大的并发数
}
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
    ++sshift;
    // 为了能够使用按位与的散列算法来定位segments数组的索引, 必须保证segments数组的长度是2的N次方
    // 例如concurrencyLevel是14, 15或者16, 那么锁的个数也总是16
    ssize <<= 1;
}
segmentShift = 32 -sshift; // 用于定位参与散列运算的位数长度
segmentMask = ssize - 1; // 散列运算的掩码, 各个二进制位都是1
this.segments = Segment.newArray(ssize); // 创建Segment数组

初始化单个segment

if (initialCapacity > MAXIMUM_CAPACITY) { // 如果自定义的Capacity超过了单个segment允许的最大的Capacity
    initialCapacity = MAXIMUM_CAPACITY; // 设定为单个segment允许的最大的Capacity
}
int c = initialCapacity / ssize; // 计算单个segment下的HashEntry数组的长度基值
if (c * ssize < initialCapacity) { // 确保数组能够容纳所有键值对
    ++c;
}
int cap = 1;
while (cap < c) {
    cap <<= 1; // 将HashEntry的长度向2的N次方对齐
}
for (int i = 0; i < this.segments.length; ++i) {
    // 初始化各个segment元素, 实际上是创建cap长度的HashEntry数组, loadFactor指定了HashEntry数组总体上允许的最大负载百分比
    this.segments[i] = new Segment<K, V>(cap, loadFactor)
}

定位Segment
在插入和获取元素的时候, 必须先通过散列算法定位到Segment,
ConcurrentHashMap首先使用Wang/Jenkins hash的变种算法对元素的hashCode进行一次再散列.

变种算法的代码如下:

// 本段代码无需理解, 只需要知道是一个对要插入ConcurrentHashMap元素的原始hash值的再散列(hash)的过程即可
private static int hash(int h) {
    h += (h << 15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h << 3);
    h ^= (h >>> 6);
    h += (h << 2) + (h << 14);
    return h ^ (h >>> 16);
}
/* 用再散列后的元素的的hash值来定位segment数组索引 */
final Segment<K, V> segmentFor(int hash) { // hash 是再散列后的元素的hash值
    // 使用hash值的高四位进行Segment数组索引的计算
    // 这里假设segmentMask为1111, segmentShift为28, 也就是Segment数组的长度为16
    return segments[(hash >>> segmetnShift) & segmentMask]; 
}

之所以进行再散列, 目的是减少散列冲突, 使元素能够均匀分布在不同的Segment上. 如若负载不均,
那么分段锁就会失去意义.

ConcurrentHashMap的操作

主要介绍三种操作: get, put和size操作

get操作

步骤如下:

  • 对元素的原始hash值先进行再散列;
  • 通过这个散列值定位到segment数组索引;
  • 通过散列算法定位到具体的HashEntry数组中的元素;
public V get(Object key) {
    int hash = hash(key.hashCode()); // 再散列
    return segmentFor(hash).get(key, hash); // 点调用segmentFor返回具体的segment, 然后通过hash值获取元素
}

需要注意的是, 定位Segment参与计算是再散列后的hash值的高位, 而定位HashEntry参与计算的是再散列后的has值的低位, 这是为了避免两次计算使用的hash值相同,
导致元素在Segment中散开了, 但是没有在HashEntry中散开.

(hash >>> segmentShift) & segmentMask; // 定位Segment数组索引使用高位
hash & (tab.length - 1); 定位HashEntry使用低位

get的高效之处在于整个get过程不需要加锁, 因为value和count都是volatile类型的值. 所以可以多线程并发读,
但是写入仍然是独占的(对每个数据段独占).

put操作

put操作是需要加锁的. 需要完成以下操作:

  1. 通过再散列的hash值定位到需要插入的Segment;
  2. 判断是否需要对Segment的HashEntry数组进行扩容(通过判断是否超过 负载因子*总容量 来判断);
  3. 定位添加元素的位置;

在扩容的时候, 首先会在当前需要扩容的Segment中创建一个容量是之前两倍的数组, 然后将原数组里的元素进行再散列后插入到新的数组里.
为了高效, ConcurrentHashMap不会对整个容器进行扩容, 仅会对某个segment进行扩容.

size操作

size操作获取count的累加值的具体步骤是:

  1. 先通过两次不锁住segment数组的来累加count;
  2. 如果统计的过程中,
    容器的count出现了变化, 则再采用加锁的方式统计Segment数组中所有元素的个数.

上述的判断容器的count是否发生变化是通过一个modCount变量来统计的, 任何会造成count变化的操作, 比如put, remove,
clean方法操作元素都会将modCount加1, 所以在进行size操作前后比较modCount元素是否发生变化,
就可以得知容器的大小是否发生变化.

ConcurrentLinkedQueue

是一个线程安全的基于链接节点的非阻塞FIFO无界队列.

对于阻塞算法, 有两种实现思路:

  1. 使用一个锁, 也就是入队出队用同一个锁
  2. 使用两个锁, 入队出队用两个锁;

对于非阻塞算法, 可以借助循环CAS的方式来实现(注: 实际上是通过加轻量级锁的方式, 循环CAS会消耗较多的时间片, 只有确认同步块运行速度较快的情况才能使用轻量级锁).

以下是ConcurrentLinkedQueue的结构

ConcurrentLinkedQueue

阻塞队列

阻塞队列是支持阻塞的插入和阻塞的移除的队列.

阻塞的插入 意思是当队列满时, 队列会阻塞插入元素的线程, 直到队列不满;
阻塞的移除 意思是在队列为空时, 获取元素的线程会等待队列变为非空;

常见的应用场景是生产者和消费者的场景, 作为二者获取元素的容器.

Java中的阻塞队列

  • ArrayBlockingQueue 一个由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue 一个由链表结构组成的有界(初始化和最大值均为Integer.MAX_VALUE大小, 可以看做无界队列)阻塞队列
  • PriorityBlockingQueue 一个支持优先级排序的无界阻塞队列
  • DelayQueue 一个使用优先级队列实现的无界阻塞队列, 在创建元素的时候可以指定多久才能从队列中获取当前元素,
    只有在延迟期满才能从队列中提取元素.
  • SynchronousQueue 一个不存储元素的阻塞队列, 也就是容量是0, 直接将生产者线程的数据传递给消费者.
    每一个put操作必须等待一个take操作, 否则会被阻塞
  • LinkedTransferQueue 一个由链表结构组成的无界阻塞队列, 如果有消费者在等待数据, 就直接将生产者的数据传递给消费者,
    如果没有消费者在等待获取数据, 就将数据放置在队列尾部.
  • LinkedBlockingDeque 一个由链表结构组成的双向阻塞队列. 由于两端都可以入队和出队, 比前述队列减少了一半的数据竞争,
    可以用在"工作窃取"模式中

未完待续

数字IC后端实战:OCC电路时钟树综合的5个关键技巧(附Synopsys工具配置) 本文深入探讨了数字IC后端设计中OCC电路时钟树综合的实战技巧。针对工具自动平衡导致的绕线拥塞和时序问题,文章提出了分段处理的核心思路,并详细阐述了通过物理隔离、分段长树策略以及Synopsys工具链(如ICC2、PrimeTime)的精准配置来优化function clk与occ clk路径的关键方法,旨在提升时序收敛性和可制造性。 阅读详情

相关推荐

Matlab Arduino 硬件支持包安装 MATLAB_Support_Package_for_Arduino_Hardware

Matlab Arduino硬件支持包离线安装

weixin_43283734的博客 2841

[Map系列]HashMap、ConCurrentHashMap、LinkedHashMap深入理解

自己思考+看源码+其他博客中整理,面试的时候答这一套还是很容易过关,但是TreeMap和weakHashMap还没整理,后面整理好了再发。 HashMap、ConcurrentHashMap、LinkedHashMap理解 一、HashMap 1.底层是数组和链表 2.参数 容量:默认大小为16 负载因子 :0.75,即当 HashMap 的 size &gt; 16*0.75 时就会发生扩容(...

qq_35643562的博客 689

Windows下用Caddy一键搞定LiveKit本地HTTPS部署(含自签名证书避坑指南)

本文详细介绍了在Windows本地开发环境中,如何利用Caddy服务器一键部署支持HTTPS和WSS的LiveKit实时通信服务。文章重点解决了自签名证书的生成与配置难题,提供了完整的避坑指南和防火墙设置,帮助开发者快速搭建安全的WebRTC测试环境,实现局域网内的实时音视频互动。

weixin_29274969的博客 583

ConcurrentHashMap使用场景、总结、对比Hashtable、ConcurrentSkipListMap、几种队列;J.U.C线程池、七个参数、四种线程池

ConcurrentHashMap使用场景 ConcurrentHashMap通常只被看做并发效率更高的Map ,用来替换其他线程安全的Map容器,比如Hashtable和Collections.synchronizedMap.线程安全的容器.特别是Map ,很多情况下一个业务中涉及容器的操作有多个,即复合操作,而在并发执行时,线程安全的容器只能保证自身的数据不被破坏,和数据在多个线程间是可见的,但无法保证业务的行为是否正确。 ConcurrentHashMap总结: ●HashMap是线程不安全的.

qq_42722241的博客 3156

aruco_ros实战:从USB摄像头到Marker位姿检测全流程解析

本文详细解析了使用aruco_ros从USB摄像头实现ArUco Marker检测与位姿估计的全流程。内容涵盖环境搭建、核心launch文件配置、可视化调试、位姿数据解析以及深度优化技巧,帮助开发者快速掌握ROS下的视觉定位关键技术,并集成到机器人项目中。

weixin_29230805的博客 265

最全卷积总结篇(分组、深度可分离、空洞、转置等)

分组卷积(Group Convolution)是一种卷积操作方式,它最早在AlexNet中引入,并在深度学习中被广泛使用,尤其是在高效模型设计(如MobileNet和ResNeXt)中。分组卷积通过将输入特征通道分为若干组,并在每组内单独进行卷积操作,可以减少计算复杂度和参数量,同时有效利用模型的结构特性。分组卷积的工作原理通道分组:输入特征图的通道图为C_in,输出特征图的通道数为C_out,将每组的C_in和C_out分为G组,每组的通道数为C_in/G和C_out/G。组内卷积。

qq_44815135的博客 4219

Java~并发容器ConcurrentHashMap、ConcurrentLinkedQueue、阻塞队列BlockingQueue的实现原理与使用

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。一个人可以走的很快,但一群人才能走的更远。

ccc6553584的博客 940

进阶06:C#连接TwinCAT 3使用ADS通讯

本文介绍了使用C#通过ADS协议与倍福TwinCAT3 PLC通讯的实现方法。主要内容包括:1)在两台电脑上安装TwinCAT3开发环境并建立网络连接;2)在TwinCAT中定义变量和结构体;3)使用C# Winform开发界面,通过TwinCAT.Ads.dll实现读写功能;4)实现主动读取和通知两种数据获取方式。详细说明了连接建立、变量定义、读写操作及事件通知的实现代码。该方案实现了对PLC寄存器数据的实时监控和操作,为工业控制系统的开发提供了参考。

weixin_38637405的博客 3688

Java并发编程中的ConcurrentHashMap与ConcurrentLinkedQueue

1.背景介绍 1. 背景介绍 在Java并发编程中,我们经常需要使用并发数据结构来实现线程安全的并发操作。这篇文章将深入探讨Java并发编程中的两个重要并发数据结构:ConcurrentHashMap和ConcurrentLinkedQueue。我们将讨论它们的核心概念、算法原理、最佳实践以及实际应用场景。 2. 核心概念与联系 2.1 ConcurrentHashMap Concurr...

AI天才研究院 1165

华为OD机试 - 单词倒序(Python/JS/C/C++ 2025 A卷 100分)

华为OD机试 2025A卷题库疯狂收录中,刷题。

学Java,找哪吒 1226

FakeNewsGPT4:通过知识增强的大规模视觉语言模型推进多模态假新闻检测

当前,多模态假新闻的大量涌现导致了显著的分布差异,这一现状亟需我们开发具备广泛适用性的检测器。然而,现有假新闻检测器因特定领域内的训练方式,难以有效获取开放世界中的事实信息。这种训练的封闭性限制了传统检测器的能力,特别是在面对跨域假新闻时,其表现往往不尽如人意。具体而言,开放世界中的假新闻在两个方面具有显著的分布差异:一方面,其操纵内容多种多样,新闻的任一部分都可能受到创意策略的操控;另一方面,真实背景复杂多变,不同地区的新闻背景和主题焦点存在显著差异。

qq_52053775的博客 822

java面试整理(四)—— HashMap、LinkedHashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别

HashMap HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键(HashMap最多只允许一条记录的键为null,允许多条记录的值为null。)。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap中不允许出现重复的键(Key) Hashmap是非线程安全的,如果多个线程同时访问一个HashMap,可能会导致数...

落尘 9783

ESP32-IDF开发之旅 基础篇⑨ ESP-IDF的FreeRTOS任务间通信和同步

本文介绍了FreeRTOS任务间通信中的队列机制,重点讲解了队列的创建、发送和接收数据的API使用方法。文章提供了详细的技术说明和示例代码,包括xQueueCreate、xQueueSend、xQueueReceive等核心函数的参数说明和返回值解释。通过一个2生产者+1消费者的实际案例,展示了如何在ESP-IDF环境下使用FreeRTOS队列实现任务间通信。示例中包含了数据发送失败处理和接收数据验证等实用技巧,为开发者提供了可参考的实现模式。

单片机菜鸟哥的博客 428

Java并发编程12--并发容器

并发容器详解

2301_81453175的博客 1048

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ConcurrentHashMap和CopyOnWriteArrayList

一、并发容器简介 并发容器 对应的普通容器 描述 ConcurrentHashMap HashMap Java 1.8 之前采用分段锁机制细化锁粒度,降低阻塞,从而提高并发性;Java 1.8 之后基于 CAS 实现。 ConcurrentSkipListMap SortedMap 基于跳表实现的 CopyOnWriteArrayList ArrayList CopyOnWriteArraySet Set 基于 CopyOnWriteArrayList实现。 Concurr

学而不思则忘 986

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【Java并发】并发集合

  ConcurrentHashMap   ConcurrentLinkedQueue    

保持学习、思考和实践 280

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

JAVA并发容器与并发工具类

JAVA并发容器与工具笔记1、JAVA并发容器2、JAVA并发容器2.1、CountDownLatch2.2、CyclicBarrier2.3、CyclicBarrier和CountDownLatch的区别2.4、Semaphore2.5、Exchanger 1、JAVA并发容器 ConcurrentHashMap :线程安全的Map。 ConcurrentLinkedQueue:线程安全非阻塞队列。采用先进先出的规 则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部;当我们获取一个元素时,

追梦的疯子 285

Java并发学习(七)并发容器

Java平台类库包含了大量的并发基础构建模块,包括线程安全的容器类以及各种用于协调多个相互协作的线程控制流的同步工具类(Synchronizer),学习这些基础模块不仅能提高并发编程的能力,更可以从中看到Doug Lea等大师的设计思想。 同步容器类 同步容器类包括Vector、Hashtable以及它们的子类,还有通过Collections.synchronizedXxx的一些工...

cai13070139328的博客 583

java并发编程之四

java并发框架之容器 ConcurrentHashMap ConcurrentHashMap比HashTable高效。HashTable的get方法和put使用同一把锁,使用的是synchronized同 步方法。这样导致在别的线程访问put方法时,访问get方法的线程也会阻塞。而ConcurrentHashMap的get方 法是不用加锁的。get是没有put那么大风险的,get只需保证

淡淡的宁静的博客 320

【Java并发编程二】Java并发包

【Java并发编程二】Java并发包 1.Java容器 1.1.同步容器 Vector ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除...

来杯特仑苏的博客 129

Java-常见并发容器-总结

在很多应用场景中,读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问List的内部数据,毕竟读取操作是安全的。这和我们之前提到过的读写锁的思想非常类似,也就是读读共享、写写互斥、读写互斥、写读互斥。JDK 中提供了类比相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致,读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。这样一来,读操作的性能就会大幅度提升。

Java程序员的知识博客 1174

并发-ConcurrentHashMap容器、ConcurrentSkipListMap 和 ConcurrentSkipListSet并发容器

目录 ConcurrentHashMap. 2 预备知识... 2 Hash. 2 位运算... 2 1.7中原理和实现... 3 ConcurrentHashMap中的数据结构... 3 初始化做了什么事?... 4 在get和put操作中,是如何快速定位元素放在哪个位置的?... 5 get()方法... 6 put()方法... 6 扩容操作... 7 size方法....

小明的Java问道之路 5992

并发的容器

JDK 提供的这些容器大部分在 java.util.concurrent 包中。 ConcurrentHashMap: 线程安全的 HashMap CopyOnWriteArrayList: 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector. ConcurrentLinkedQueue: 高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,这是一个非阻塞队列。 BlockingQueue: 这是一个接口,JDK 内部通过链表、数组等方式实现了这个

li237的博客 117

Java学习笔记-并发编程-1.1并发容器类

1.1并发容器类 同步类容器 同步类容器都是线程安全的,但在某些场景下可能需要加锁来保护复合操作。 复合类操作如:迭代(反复访问元素,遍历完容器中所有的元素)、跳转(根据指定的顺序找到当前元素的下一个元素)、以及条件运算。 这些复合操作在多线程并发地修改容器时,可能会表现出意外的行为,最经典的便是ConcurrentModificationException,原因是当容器迭代的过程中,被并发的修改了内容,这是由于早期迭代器设计的时候并没有考虑并发修改的问题。 使用同步类容器的时候,如果使用增强for循环或

CarSkyDragn的博客 407

java并发编程的艺术

文章目录 1. JAVA并发机制的底层实现原理 1. 锁的状态 2. 原子操作的实现原理 2. java内存模型 1. 基础 2. 重排序 4. JAVA并发编程基础 1. Synchronized底层指令 2. Thread.join 5. JAVA中的锁 1. AbstractQueuedSynchronized(AQS, 同步器) 2. LockSupport 3. Condition 6. JAVA并发容器和框架 1. ConcurrentHashMap 2. ConcurrentLinkedQue

黑马程序员广州中心的专栏 130

java 并发编程艺术摘抄

读完后对其中部分内容表示理解并认同,于是便抄下来以便后来参考。

有趣 1423
上一篇: Java解惑 - sleep和wait/notify机制, 线程阻塞和等待的区别
下一篇: Java并发编程 - 锁 - 原理详解
congyh
博客等级 码龄15年 43粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值