首页 > 其他分享 >ConcurrentHashMap高频问题

ConcurrentHashMap高频问题

时间:2023-07-21 15:45:18浏览次数:38  
标签:扩容 链表 ConcurrentHashMap 问题 线程 数组 红黑树 高频

1:HashMap为啥线程不安全?

问题1:JDK1.7里有环(扩容时)。并发+链表头插入导致的,  1.8后改为链表尾插入

问题2:数据会覆盖,数据可能丢失。

问题3:其次计数器,也是传统的++,在记录元素个数和HashMap写的次数时,记录不准确。

问题4:数据迁移,扩容,也可能会丢失数据。

2 ConcurrentHashMap如何保证线程安全的?

回答1:尾插,其次扩容有CAS保证线程安全

回答2:写入数组时,基于CAS保证安全,挂入链表或插入红黑树时,基于synchronized保证安全。

回答3:这里ConcurrentHashMap是采用LongAdder实现的技术,底层还是CAS。(AtomicLong)

回答4:ConcurrentHashMap扩容时,一点基于CAS保证数据迁移不出现并发问题, 其次ConcurrentHashMap还提供了并发扩容的操作。举个例子,数组长度64扩容为128,两个线程同时扩容的话,

线程A领取64-48索引的数据迁移任务,线程B领取47-32的索引数据迁移任务。关键是领取任务时,是基于CAS保证线程安全的。

3 ConcurrentHashMap构建好,数组就创建出来了吗?如果不是,如何保证初始化数组的线程安全?

ConcurrentHashMap是懒加载的机制,而且大多数的框架组件都是懒加载的~

基于CAS来保证初始化线程安全的,这里不但涉及到了CAS去修改sizeCtl的变量去控制线程初始化数据的原子性,同时还使用了DCL,外层判断数组未初始化,中间基于CAS修改sizeCtl,内层再做数组未初始化判断。

4 为什么负载因子是0.75,为什么链表长度到8转为红黑树?

而且ConcurrentHashMap的负载因子不允许修改!

负载因子是0.75从两个方面去解释。

为啥不是0.5,为啥不是1?

0.5:如果负载因子是0.5,数据添加一半就开始扩容了

  • 优点:hash碰撞少,查询效率高。

  • 缺点:扩容太频繁,而且空间利用率低。

1:如果负载因子是1,数据添加到数组长度才开始扩容

  • 优点:扩容不频繁,空间利用率可以的。

  • 缺点:hash冲突会特别频繁,数据挂到链表上,影响查询效率,甚至链表过长生成红黑树,导致写入的效率也收到影响。

0.75就可以说是一个居中的选择,两个方面都兼顾了。

再聊就是泊松分布,在负载因子是0.75时,根据泊松分布得出,链表长度达到8的概率是非常低的,源码中的标识是0.00000006,生成红黑树的概率特别低。

虽然ConcurrentHashMap引入了红黑树,但是红黑树对于写入的维护成本更高,能不用就不用,HashMap源码的注释也描述了,要尽可能规避红黑树。

至于6退化为链表,是因为满树是7个值,7不退化是为了防止频繁的链表和红黑树转换,这里6退化留下了一个中间值,避免频繁的转换。

put操作太频繁的场景,会造成扩容时期put的阻塞 ?

一般情况下不会造成阻塞。

因为如果在put操作时,发现当前索引位置并没有数据时,正常把数据落到老数组上。

如果put操作时,发现当前位置数据已经被迁移到了新数组,这时无法正常插入,去帮助扩容,快速结束扩容操作,并且重新选择索引位置查询

5 ConcurrentHashMap何时扩容,扩容的流程是什么?

  • ConcurrentHashMap中的元素个数,达到了负载因子计算的阈值,那么直接扩容

  • 当调用putAll方法,查询大量数据时,也可能会造成直接扩容的操作,大量数据是如果插入的数据大于下次扩容的阈值,直接扩容,然后再插入

  • 数组长度小于64,并且链表长度大于等于8时,会触发扩容

扩容的流程:(sizeCtl是一个int类型变量,用于控制初始化和扩容)

  • 每个扩容的线程都需要基于oldTable的长度计算一个扩容标识戳(避免出现两个扩容线程的数组长度不一致。其次保证扩容标识戳的16位是1,这样左移16位会得到一个负数)

  • 第一个扩容的线程,会对sizeCtl + 2,代表当前有1个线程来扩容

  • 除去第一个扩容的线程,其他线程会对sizeCtl + 1,代表现在又来了一个线程帮助扩容

  • 第一个线程会初始化新数组。

  • 每个线程会领取迁移数据的任务,将oldTable中的数据迁移到newTable。默认情况下,每个线程每次领取长度为16的迁移数据任务

  • 当数据迁移完毕时,每个线程再去领取任务时,发现没任务可领了,退出扩容,对sizeCtl - 1。

  • 最后一个退出扩容的线程,发现-1之后,还剩1,最后一个退出扩容的线程会从头到尾再检查一次,有没有遗留的数据没有迁移走(这种情况基本不发生),检查完之后,再-1,这样sizeCtl扣除完,扩容结束。

6 ConcurrentHashMap得计数器如何实现的?

这里是基于LongAdder的机制实现,但是并没有直接用LongAdder的引用,而是根据LongAdder的原理写了一个相似度在80%以上的代码,直接使用。

LongAdder使用CAS添加,保证原子性,其次基于分段锁,保证并发性。

7 ConcurrentHashMap的读操作会阻塞嘛?

无论查哪,都不阻塞。

查询数组? :第一块就是查看元素是否在数组,在就直接返回。

查询链表?:第二块,如果没特殊情况,就在链表next,next查询即可。

扩容时?:第三块,如果当前索引位置是-1,代表当前位置数据全部都迁移到了新数组,会调用find方法去新数组查询,不管有没有扩容完。

查询红黑树?:如果有一个线程正在写入红黑树,此时读线程还能去红黑树查询吗?因为红黑树为了保证平衡可能会旋转,旋转会换指针,可能会出现问题。所以在转换红黑树时,不但有一个红黑树,还会保留一个双向链表,此时会查询双向链表,不让读线程阻塞。至于如何判断是否有线程在写,和等待写或者是读红黑树,根据TreeBin的lockState来判断,如果是1,代表有线程正在写,如果为2,代表有写线程等待写,如果是4n,代表有多个线程在做读操作。

 

 

 

 

 

 

 

 

标签:扩容,链表,ConcurrentHashMap,问题,线程,数组,红黑树,高频
From: https://www.cnblogs.com/mytzq/p/17571542.html

相关文章

  • squid 503问题排查,即ipv6下的squid应用
    squid不支持IPv6,按照里面的提示,在/etc/squid/squid.conf里面配置一个dns_v4_firston再次尝试的时候可以了!如果还是不行的话,直接修改系统的配置修改/etc/sysconfig/network:设置NETWORKING_IPV6=no......
  • HJ43 迷宫问题
    1.题目读题 HJ43 迷宫问题 考查点 2.解法思路 代码逻辑 具体实现importjava.util.ArrayList;importjava.util.List;publicclassMain{publicstaticvoidmain(String[]args){//迷宫地图int[][]maze={......
  • 解决element ui 下拉框表单验证切换选项就直接触发的问题
    elementui下拉框表单验证正确使用步骤1.确保form组件的:model属性绑定了表单的数据对象  2.确保form组件的rules绑定了对应的rule 3.确认要验证的表单item绑定了对应的prop属性注意:prop属性的名称要和rule里面的名称一样并且和v-model的属性名称一样才行 完成以上......
  • 多表查询和left join需要注意的问题
    一、多表查询1、内连接隐式内连接使用一张以上的表做查询就是多表查询语法:SELECT{DISTINCT}*|列名..FROM表名别名,表名1别名 {WHERE限制条件ORDERBY排序字段ASC|DESC...}范例:emp表DROPTABLE"SCOTT"."EMP";CREATETABLE"SCOTT"."EMP"("EMPNO"NUMBE......
  • java Apollo配置和yml配置同时存在的问题
    当JavaApollo配置和yml配置同时存在时,可能会导致以下问题:1.配置冲突:JavaApollo和yml配置文件可能定义了相同的配置项,导致冲突或覆盖。这可能会导致应用程序在运行时的行为与预期不同。2.配置失效:如果JavaApollo和yml配置文件中定义了相同的配置项,且两者的值不一致,那么最终生效......
  • DBUtils不同版本的问题
    DBUtils版本问题前言事情的起因是,原本在pycharm上开发的代码,因为要使用到线程池,所以就按安装了DBUtils,在windows上运行代码倒没什么问题,后因代码运行时需要占用的内存过多,所以代码要转移到Linux服务器上,问题由之而来,运行代码时总会会报出找不到DBUtils库的错误,经过几番反复确认......
  • LeetCode 347. 前 K 个高频元素
    快排思想注意,这里是倒序排序,因此应该while(nums[i].cnt>x);classSolution{public:structelement{intval,cnt;element(inta,intb){val=a;cnt=b;}};vector<int>res;voidquick......
  • codility算法题:猫过桥问题
    1.题目读题  考查点 2.解法思路 代码逻辑 具体实现 publicclassSolutions{publicstaticvoidmain(String[]args){System.out.println(solution(10,newint[]{2,3,4,8},newint[]{2,5}));System.out.println(solution(10,......
  • 解决Clipse Java内存溢出问题的几种方案
    解决ClipseJava内存溢出问题的几种方案随着Java应用程序的复杂度不断提高,内存溢出成为一个常见的问题。当应用程序超出了可用内存资源时,就会发生内存溢出错误。而在ClipseJava编程开发中,也常常会遇到这样的问题。为了解决这个问题,本文将介绍一些可行的解决方案。1.增加JVM堆......
  • 在Vue3中,解决 Echart tooltip 不显示的问题
    为什么在Vue中使用ECharts时图表显示异常?Vue3,中使用reactive及ref会导致ECharts的对象实例被代理成为响应式对象,影响ECharts对内部属性的访问,可能会导致图表无法正确显示等一系列意外问题,且会由于深度监听而极大地降低图表展示性能。解决方案为:使用普通变量声明ECh......