首页 > 其他分享 >hashmap线程不安全 ConcurrentHashMap是如何实现线程安全的

hashmap线程不安全 ConcurrentHashMap是如何实现线程安全的

时间:2024-01-29 20:25:40浏览次数:17  
标签:ConcurrentHashMap hash hashmap 安全 线程 put Segment

 

 

 

2、JDK1.8中的数据覆盖

(1)dk1.7的数据丢失、死循环问题在JDK1.8中已经得到了很好的解决,直接在HashMap的resize()中完成了数据迁移。

(2)为什么说 JDK1.8会出现数据覆盖的情况?
查看这段JDK1.8中的put操作代码:
在这里插入图片描述
如下图框中的代码是判断是否出现hash碰撞,假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完该行判断代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。

                        
原文链接:https://blog.csdn.net/qq_46074155/article/details/120072178

 

 

ConcurrentHashMap是怎么做到线程安全的?

    get方法如何线程安全地获取key、value?
    put方法如何线程安全地设置key、value?
    size方法如果线程安全地获取容器容量?
    底层数据结构扩容时如果保证线程安全?
    初始化数据结构时如果保证线程安全?

ConcurrentHashMap并发效率是如何提高的?

    和加锁相比较,为什么它比HashTable效率高?
而ConcurrentHashMap则使用了锁分段(减小锁范围)、CAS(乐观锁,减小上下文切换开销,无阻塞
                        多个线程同时进行put操作,在初始化数组时使用了乐观锁CAS操作来决定到底是哪个线程有资格进行初始化,其他线程均只能等待。

用到的并发技巧:

    volatile变量(sizeCtl):它是一个标记位,用来告诉其他线程这个坑位有没有人在,其线程间的可见性由volatile保证。
    CAS操作:CAS操作保证了设置sizeCtl标记位的原子性,保证了只有一个线程能设置成功
减小锁粒度:将Node链表的头节点作为锁,若在默认大小16情况下,将有16把锁,大大减小了锁竞争(上下文切换),就像开头所说,将串行的部分最大化缩小,在理想情况下线程的put操作都为并行操作。同时直接锁住头节点,保证了线程安全
                        

如何实现呢?这就用到了ConcurrentHashMap中最关键的Segment。

ConcurrentHashMap中维护着一个Segment数组,每个Segment可以看做是一个HashMap。

而Segment本身继承了ReentrantLock,它本身就是一个锁。

在Segment中通过HashEntry数组来维护其内部的hash表。

每个HashEntry就代表了map中的一个K-V,用HashEntry可以组成一个链表结构,通过next字段引用到其下一个元素。

0.3 线程安全的扩容(Rehash)

HashMap的线程安全问题大部分出在扩容(rehash)的过程中。

ConcurrentHashMap的扩容只针对每个segment中的HashEntry数组进行扩容。

由上述put的源码可知,ConcurrentHashMap在rehash的时候是有锁的,所以在rehash的过程中,其他线程无法对segment的hash表做操作,这就保证了线程安全
原文链接:https://blog.csdn.net/qq_41737716/article/details/90549847

标签:ConcurrentHashMap,hash,hashmap,安全,线程,put,Segment
From: https://www.cnblogs.com/JavaYuYin/p/17995246

相关文章

  • 线程的三种创建方式
    1//方式一:继承Thread类2publicclassThread{3publicstaticvoidmain(String[]args){4Threadt=newMyThread();5t.start();67for(inti=0;i<5;i++){8System.out.println("主线程:"+......
  • java用多线程批次查询大量数据(Callable返回数据)方式
    我看到有的数据库是一万条数据和八万条数据还有十几万条,几百万的数据,然后我就想拿这些数据测试一下,发现如果用java和数据库查询就连一万多条的数据查询出来就要10s左右,感觉太慢了。然后网上都说各种加索引,加索引貌似是有查询条件时在某个字段加索引比较快一些,但是毕竟是人家的库不......
  • 线程分批处理数据及MyBatis的批量插入
    文章目录一、背景二、代码实现:三、分页查询下游批次处理场景四、MyBatis的批量插入1、活动表简单表结构:2、业务层组装数据:一、背景数据量较多时,我们常常遇到需要分批处理的情况,比如上千上万数据需要需要操作数据库时(入库或者更新),我们想到分批处理,或者解析文件数据量较多,我......
  • 线程安全问题
    需求:某电影院目前正在上映国产大片,共100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票publicclassMyThreadextendsThread{//表示这个类的所有对象都共享ticketstaticintticket=0;//0~99@Overridepublicvoidrun(){while(true){if(tic......
  • 线程池参数千万不要这样设置,坑得我整篇文章都写错了,要注意!
    你好呀,我是歪歪。先给大家道个歉:上周不是发布了这篇文章嘛:《三个烂怂八股文,变成两个场景题,打得我一脸懵逼。》其中第一个关于线程池的场景,经过读者提醒可能有问题,我又一次用尽浑身解数分析了一波,发现之前确实分析的不对。这个案例真的是再一次深入的刷新了我对于线程池运行过......
  • 深入浅出Java多线程(二):Java多线程类和接口
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第二篇内容:Java多线程类和接口。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在现代计算机系统中,多线程技术是提升程序性能、优化资源利用和实现并发处理的重要手段。特别是在Java编程语言中,多线程机......
  • 写一段rust代码,两个线程共享一个bool变量,一个写,一个读
    usestd::sync::{Arc,Mutex};usestd::thread;fnmain(){//创建一个布尔变量并用Arc和Mutex包装,使其可在多个线程间共享和修改letshared_bool=Arc::new(Mutex::new(false));//克隆Arc变量,以便在两个线程之间共享letwriter_shared_bool=Ar......
  • Qt/C++音视频开发64-共享解码线程/重复利用解码/极低CPU占用/画面同步/进度同步
    一、前言共享解码线程主要是为了降低CPU占用,重复利用解码,毕竟在一个监控系统中,很可能打开了同一个地址,需要在多个不同的窗口中播放,形成多屏渲染的效果,做到真正的完全的画面同步,在主解码线程中切换了播放进度,所有关联的同一个解码线程的播放窗体也会立即同步画面,使得感官上看起来......
  • C# 线程本地存储 为什么线程间值不一样
    一:背景1.讲故事有朋友在微信里面问我,为什么用ThreadStatic标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类似疑问的朋友解个惑吧。二:为什么值不一样1.问题复现为了方便讲述,定义......
  • 多线程
    多线程理论(1)什么是线程在Python中,线程(Thread)是执行单元的最小单位。线程是进程内的一条执行路径,每个线程都有自己的执行序列、执行环境和栈空间,但它们共享同一个进程的地址空间。在多线程编程中,可以同时运行多个线程,每个线程执行不同的任务,从而实现并发执行。相比于多进......