【JaveEE】——多线程中使用顺序表,队列,哈希表

 

8e19eee2be5648b78d93fbff2488137b.png

阿华代码,不是逆风,就是我疯

你们的点赞收藏是我前进最大的动力!!

希望本文内容能够帮助到你!!

目录

一:多线程环境使用ArrayList

引入:

1:顺序表使用同步机制

2:套壳封装

3:写时拷贝

(1)添加/修改元素操作

(2)优点

(3)缺点

(4)使用场景

二:多线程环境使用队列

三:多线程环境使用哈希表(面试高频)

引入

1:ConcurrentHashMap

(1)缩小锁粒度

(2)使用CAS原子操作

(3)扩容的优化


一:多线程环境使用ArrayList

引入:

原来的集合类,大部分都是线程不安全的,但是有几个例外:Vector,Stack,HashTable(这几个类)但是现在官方已经不太推荐使用了,后续可能会删掉——因为哪怕是在单线程下也要加锁,这种情况不合理(往下看)

在这些类内部中,把一些关键的方法都加锁了,导致它们不仅在多线程场景下要加锁,而且在单线程场景下也要加锁。虽然JVM中有“锁消除”机制,但这也不是万能的,加锁带来的资源消耗依旧是不可忽视的(单线程下就没必要加锁了嘛)

1:顺序表使用同步机制

使用synchronized和ReentrantLock进行加锁,上一篇文章有提及两者的区别,往回翻翻~~

2:套壳封装

使用Collections.synchronizedList(new ArrayList)

因为ArrayList本身各种操作都是不带锁的,我们把它作为参数传入,相当于给ArrayList封装一下,套入Collections.synchronizedList()这个壳中,得到一个新的对象,这个新的对象调用关键的方法操作都是带有锁的

3:写时拷贝

使用CopyOnWriteArrayList

(1)添加/修改元素操作

如果我们往一个容器里面添加元素,我们不往这个容器中添加,而是先copy一份新容器,往新的容容里面添加或者修改元素

添加修改完元素之后,在将引用指向新的容器

(2)优点

①可以进行并发读在“读多写少”的场景下效率非常高(在引用指向新的容器之前,读操作都可以在旧容器上完成)

(3)缺点

①相应的顺序表如果太大,copy的开销也变高了

②“写操作”非常频繁,copy的频率就会非常高,资源的消耗和占用就比较严重

③不能第一时间读到新写的数据

(4)使用场景

服务器加载配置文件的时候,就会把文件内容解析出来放到内存的数据结构中,配置文件体积小,而且修改频率低

二:多线程环境使用队列

这边以前的文章有总结过就不再加以详述

主要以自己加锁和使用BlockingQueue为主

三:多线程环境使用哈希表(面试高频)

引入

在多线程环境下,Hashtbale是线程安全的,因为在Hashtable内部的关键方法中都有进行synchronized加锁操作。

但是HashMap就不行了,在数组的基础上还涉及到链表和树化

1:ConcurrentHashMap

于是我们就加以改进,引入了ConcurrentHashMap(并发HashMap),以下是我们的改进过程

(1)缩小锁粒度

①HashMap中的加锁

当我们尝试对HashMap中的不同链表下的不同元素进行修改操作的时候,就会触发锁竞争

因为这个锁是针对整个HashMap(this)而言的

如下图中我们想修改元素1和元素2,就会触发锁竞争,

重点:

但实际上修改不同链表上的元素操作,并不会触发线程安全问题(加大了加锁的频率,资源浪费)

只有在修改同一个链表下(相邻必触发:因为操作会涉及到同一个引用)元素可能会触发线程安全问题

于是我们在ConcurrentHashMap中进行了优化

4f64479706144ca0a0f509496a1a3c67.png

②ConcurrentHashMap中的加锁

锁对象为每个数组中的元素(链表的头结点),此时如果修改同一个链表下的元素,就会触发锁竞争。

理解:相当于把一个大锁拆分成了好多把小锁(这就是缩小锁粒度)

优点:不仅解决了线程安全问题,还降低了加锁的频率,节约了资源

注:这里的锁的数量虽然很多,但并不会增加太多的资源消耗,因为加锁对象(头结点)是现成的,不需要我们再去创建了

63314c4759f84d7bbc2ba5e40457f38e.png

(2)使用CAS原子操作

在ConcurrentHashMap中,比如针对哈希表中的元素个数的维护,我们使用CAS就可以减少一些加锁。

用synchronized加锁,咱们不知道加锁处于那种阶段(程度)的加锁——可能是偏向锁,轻量级化加锁,甚至是最后升级为重量级化加锁,这件事都是不可预估的

(3)扩容的优化

负载因子=元素个数/数组长度  ,0.75是一个扩容阈值指标

①HashMap扩容机制

如果数组元素个数太多会进行扩容,链表下元素个数太多会进行树化

扩容:创建一个更大数组,把旧Hash表上的元素一下子搬过去(一把梭哈),如果元素数量非常多,这里的copy操作就会非常的耗费时间,实际表现就是突然间某个操作非常卡

②ConcurrentHashMap扩容机制

扩容时,每次只搬运一部分元素,随着每次的插入/删除/添加/查找操作,都会搬运一部分元素。

内部机制:扩容时,有两份哈希表

插入操作——往新表上插

删除操作——新表旧表都删

查找操作——新表旧表都查

优点:确保每次操作耗费的时间都不长,避免出现卡顿的情况

缺点:整体扩容的时间变长了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值