多线程(进阶四:线程安全的集合类)

目录

一、多线程环境使用ArrayList

二、多线程环境使用队列

三、多线程环境使用哈希表

1、HashMap

2、Hashtable

3、ConcurrentHashMap

(1)缩小了锁的粒度

(2)充分使用了CAS原子操作,减少一些加锁

(3)针对扩容操作的一些优化(化整为零)

四、相关面试题


大部分集合类都是线程不安全的,Vector,Stack,Hashtable是线程安全的,但不建议使用,因为无论什么情况都要加锁,甚至单线程也是,这样就很不合理;并且这几个集合类官方已经不推荐使用了,可能在未来的版本中就被删掉了。

下面介绍一些线程不安全的集合类。

一、多线程环境使用ArrayList

1、自己使用同步机制(synchronized或者ReentrantLock)

2、Collections.synchronizedList(new ArrayList);

        相当于给ArrayList套了个壳,ArrayList各种操作本身是不带锁的,通过上述操作套壳后,得到了新的对象,新的对象里面的关键方法都是带有锁的。

3、使用CopyOnWriteArrayList

        CopyOnWrite容器即写时复制的容器,多线程对这个顺序表进行读操作时,不会有线程安全问题,但是当多线程进行写操作时,就会有线程安全问题,CopyOnWriteArrayList会复制一份原来的顺序表,并且修改新的顺序表内容,再把原来的引用指向新的顺序表(此操作是原子的,不需要加锁)。


二、多线程环境使用队列

1、自己加锁

2、使用BlockingQueue

1. ArrayBlockingQueue
基于数组实现的阻塞队列
2. LinkedBlockingQueue
基于链表实现的阻塞队列
3. PriorityBlockingQueue
基于堆实现的带优先级的阻塞队列
4. TransferQueue
最多只包含⼀个元素的阻塞队列


三、多线程环境使用哈希表

1、HashMap

        HashMap本身就是线程不安全的。

2、Hashtable

        在一些关键方法上加了锁

        这也相当于对this加了锁,也就是针对Hashtable对象本身加锁,如果尝试修改Hash表中两个不同Hash值里的链表,会发生锁冲突。如图:

3、ConcurrentHashMap

相对于Hashtable,进行了些优化。

(1)缩小了锁的粒度

        多线程如果修改Hash表里Hash值不同的链表都发生锁冲突,是不合理的,而且锁冲突是很耗时的,所以ConcurrentHashMap是对Hash表里每个链表都进行加锁,这样,不同的链表有不同的锁对象,多线程修改两个不同的链表,就不会发生锁冲突了,如图:

注意:更多的锁并不意味着要耗费更多的空间,因为在java中的任何对象都可以作为锁对象,而本身Hash表中就得有数组,数组元素都已经存在,即链表的头结点,每个链表都有一个头结点,可以直接把这个头结点作为链表的锁对象。

(2)充分使用了CAS原子操作,减少一些加锁

        比如,针对Hash表元素个数的维护。

(3)针对扩容操作的一些优化(化整为零)

        负载因子:描述了每个桶(Hash表)平均有多少个元素;公式:实际个数 / 数组长度(桶的个数)。0.75是默认的扩容阈值(也可以是其他数字值),如果我们算出的负载因子超过规定的扩容阈值,Hash表就会进行扩容。

        进行扩容时,如果不是concurrentHashMap,会创建一个更大是数组,把旧的数组元素搬运到新的数组中,一次性的全部搬运完,如果Hash表本身的元素就非常多,这里扩容就会非常耗时,但可能过一会儿就又好了,存在不稳定因素,我们无法控制Hash表何时触发扩容。

        concurrentHashMap则不是一次性的全部搬运完,而是把Hash表中的元素分为若干次搬运完,而不是直接一次性梭哈完,假设Hash表有1kw个元素,每次就只搬运5k哥元素,一共花费2k次搬运完成(搬运的时间会更长一些),但能确保每次搬运消耗的时间不会很长,避免出现很卡的情况。

总的来说,扩容是一个低频的操作(前提把扩容阈值设置合理),运行整个程序,可能一天都不会触发扩容,触发了每次可能会花费几分钟的时间进行搬运。

注意:在扩容过程中,存在两份Hash表,一份是新的,一份是旧的。

        进行插入操作,直接往新的Hash表上插入。

        进行删除操作,新的旧的都要删除。

        进行查找操作,新的旧的都要查找。


四、相关面试题

1.ConcurrentHashMap的读是否要加锁,为什么?

 读操作没有加锁.目的是为了进一步降低锁冲突的概率.为了保证读到刚修改的数据,搭配了volatile关键字.

2.介绍下ConcurrentHashMap的锁分段技术?

这个是Java1.7所采取的技术.Java1.8中已经不再使用了.简单的说就是把若干个哈希桶分成一个"段"(Segment),针对每个段分别加锁.

目的也是为了降低锁冲突的概率.当两个线程访问的数据恰好在同一个段上时,才会触发锁竞争

3.ConcurrentHashMap在jdk1.8做了哪些优化?

取消了分段锁,直接给每个哈希桶(每个链表)分配了一个锁(就是以每个链表的头节点对象作为锁对象).

将原来的数组 + 链表的实现方式改进成 数组 + 链表 /红黑树的方式.当链表较长的时候(大于等于8个元素)就转换成红黑树. 

4.HashMap和HashTable,ConcurrentHashMap之间的区别?

HashMap: 线程不安全.key允许为null

HashTable:线程安全.使用synchronized锁HashTable对象,效率较低.key不允许设置为null.

ConcurrentHashMap: 线程安全.使用synchronized锁每个链表的头节点,锁冲突概率较低,充分利用CAS机制,优化了扩容方式.key不允许为null. 


都看到这了,点个赞再走吧,谢谢谢谢谢

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C初阶】预处理

前言:在本文中,我将系统的整理一下C语言关于预处理部分的语法,便于整理与回归。 1.预定义符号 在C语言中,C标准提供里一些C预定义符号,在预处理期间完成,可以直接使用。 有如下几个符号: 2.#…

MQL5学习之RSI指标编写

研究MT5时发现MQL5这个指标编写功能很强大,应该是碾压国内所有的指标系统,不过这个东西相对复杂很多,比通达信公式不知复杂几许,看起来和C语法接近,倒是比较适合自己。试着玩一下,发现还是有点难度的。索性…

Snagit 2024:让你的屏幕活动瞬间变得生动有力 mac/win版

Snagit 2024 屏幕录制与截图软件是一款功能强大的工具,专为现代用户设计,以满足他们在工作、学习和娱乐中对屏幕内容捕捉和分享的需求。这款软件结合了屏幕录制和截图功能,为用户提供了一种高效、便捷的方式来捕捉屏幕上的精彩瞬间。 Snagit…

相关知识1111

一、 店铺编号和相关负责人 1、天猫兄弟、锦格 京东凡越 福林哥 如萍姐 2、京东锦格 天猫凡越 林森 雷佳华 3、天猫从简 京东从简 孔哥 4、抖音锦格 拼多多凡越 鸿哥 不知道哪个店铺编号:0 二、天猫京东聊天界面快捷搜索商品 1、 天猫只能根据标题搜索 2、京东是…

洛谷P1509找啊找啊找GF

题解:这题我们需要考虑两个因素 ,既要有钱,也需要有人品,但是呢,还想花最少得时间泡到最多的女生,那么这题我们就要用到以往的二维dp数组,但是真的是二维的吗?不,因为要考…

2024国际元宇宙博览会:阿里元境以元宇宙数字内容助力文旅数字化发展

2月26日,MES2024国际元宇宙博览会在深圳会展中心正式开幕,大会以“向3D出发,元宇宙来袭,电竞娱乐正当时”为主题,聚焦元宇宙产业链,以“汇聚企业创新,助力产业重构,推动行业发展”为…

C++ //练习 10.19 用stable_partition重写前一题的程序,与stable_sort类似,在划分后的序列中维持原有元素的顺序。

C Primer(第5版) 练习 10.19 练习 10.19 用stable_partition重写前一题的程序,与stable_sort类似,在划分后的序列中维持原有元素的顺序。 环境:Linux Ubuntu(云服务器) 工具:vim …

IO多路转接

1.select 初识select 系统提供 select 函数来实现多路复用输入 / 输出模型 . select 系统调用是用来让我们的程序监视多个文件描述符的状态变化的 ; 程序会停在 select 这里等待,直到被监视的文件描述符有一个或多个发生了状态改变 ; select函数模型 select的函…

springboot236基于springboot在线课程管理系统的设计与实现

基于SpringBoot在线课程管理系统的设计与实现 摘要 本文首先介绍了在线课程管理系统的现状及开发背景,然后论述了系统的设计目标、系统需求、总体设计方案以及系统的详细设计和实现,最后对在线课程管理系统进行了系统检测并提出了还需要改进的问题。本系…

【Vue3】解锁Vue3黑科技:探索接口、泛型和自定义类型的前端奇迹

💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢…

volatile 关键字 (一)

volatile 关键字 (一) 文章目录 volatile 关键字 (一)如何保证变量的可见性?如何禁止指令重排序? 文章来自Java Guide 用于学习如有侵权,立即删除 如何保证变量的可见性? 在 Java 中…

k8s Pod基础(概念,容器功能及分类,镜像拉取和容器重启策略)

目录 pod概念 Kubernetes设计Pod概念和特殊组成结构的用意 Pod内部结构: 网络共享: 存储共享: pause容器主要功能 pod创建方式 pod使用方式 pod分类 pod的容器分类 基础容器(infrastructure container)&…