java的各种集合为什么不安全(List、Set、Map)?( 二 )
步骤很清楚 , 如果有了写操作 , 需要加锁:
- 加锁
- 获取到当前的集合数组;
- 计算长度;
- 调用 Arrays.copyOf 方法进行添加操作 , 每次只添加一个元素进去;
- 修改引用 , 更新最新的集合;
- return true 。
- 解锁
文章插图
可以看到是一个普通的 Object 。
那么加锁的时候就用 synchronized 对 Object 进行加锁 , 没有采用 juc 的 ReetrantLock , 注释li也写了 , 偏向于使用内置的 monitor 也就是 synchronized 底层 monitor 锁 , 这一点也充分说明了 synchronized 的性能更新使得源码作者使用它 。
这个方法是处理最直接的 , 其他对应的写操作:remove、set等等也是一样的基础流程 。
我们再来看看读操作 get 方法:
文章插图
2|0二、HashSet 的不安全2|12.1 问题及原因我们还是用 List 一样的测试代码;
public class TestSet {public static void main(String[] args) {HashSet set = new HashSet<>();for (int i = 0; i < 100; i++){new Thread(()->{set.add(UUID.randomUUID().toString().substring(0,8));System.out.println(set);},String.valueOf(i)).start();}}}
就会看到一样的错误:文章插图
2|22.2 出现问题的原因其实从出现 ConcurrentModificationException 异常来看 , 我们可以猜测是和 List 类似的原因导致的异常 。
可以看到 , 源码里面 , Set 的底层维护的是一个 HashMap 来实现 。 对于遍历操作来说 , 都是一样的使用了 fail-fast iterator 迭代器 , 因此会出现这个异常 。
另外 , 因为 HashSet 的底层是 HashMap, 本质上 , 对于每一个 key, 保证唯一 , 使用了一个 value 为 PRESENT 常量的键值对进行存储 。
文章插图
put 的过程也是调用 map 的 put 方法 。
2|32.3 解决方案
- List 有对应的 Vector 可用 , 本来就是线程安全的集合 , 但是 Set 没有;
- 数据量小的时候 , 使用 Collections.synchronizedSet(new HashSet<>()) 这种方式 , 来包裹这个集合 , 上面我们使用 List 的时候也有类似的方法;
- 同样的 , juc包为我们提供了新的线程安全集合 CopyOnWriteArraySet() 。
- 按照前面的思路 , List 的对应线程安全集合是在 List 集合的数组基础上进行加锁的相关操作 。
- 那么 Set 既然底层是 HashMap , 对应的线程安全集合就应该是对 HashMap 的线程安全集合进行加锁 , 或者说直接用 ConcurrentHashMap 集合来实现 CopyOnWriteArraySet。
- 但事实上 , 源码并不是这么做的 。
文章插图
可以看到 , 他的思路没有顺延 util 包的 HashSet 的实现思路 , 而是直接使用了 CopyOnWriteArrayList 作为底层数据结构 。 也就是说没有利用 Map 的键值对映射的特性来保证 set 的唯一性 , 而是用一个数组为基底的列表来实现 。 (那显然在去重方面就要做额外的操作了 。 )
然后每一个实现的方法都很简单 , 基本是直接调用了 CopyOnWriteArrayList 的方法:
文章插图
我们最担心的可能 产生问题的 remove 和 add 方法 , 也是使用了 CopyOnWriteArrayList 的方法:
而保证 set 的不重复性质的关键 , 显然就在于 CopyOnWriteArrayList 的 addIfAbsent 方法 , 我们还是点进 CopyOnWriteArrayList 源码看一看这个方法的实现:
文章插图
其中的 indexOfRange 方法:
文章插图
可以看到 , 也是加了 Monitor 锁来进行的 , 整个过程是这样的:
- 获取本来的 set, 是一个数组 , 以快照形式返回当前的数组;
- indexOfRange 方法通过遍历查找查找元素出现位置 , addIfAbsent方法完成不存在则加入 , 如果前一个为 false 后一个就不会执行;
- 小米科技|性价比拉满!TCL T8E-PRO QLED智屏当属潮玩世代的必备单品
- 华为鸿蒙系统|华为汽车战略布局,进入汽车行业的底气来自哪里?(车车佳)
- 浙江省|浙江的五大富豪,四位做过中国首富,仅马云的阿里就1年纳税366亿
- iOS|恒创科技:Linux日本云服务器安全设置的基本步骤
- javascript|手机移动端的PyTorch来了,还支持JavaScript
- 中关村|柳传志在这里被骗、掘金,书写半部科技史的中关村经历了什么?
- 手机维修|手机维修的猫腻‖你是不是上当了?
- 智能化|感知局限下,车路协同的“子弹”还得再飞会儿
- 华为鸿蒙系统|都2021年底了,为何Mate40Pro还是目前公认最好用的“安卓”手机
- saas|上半年的Redmi K40 Pro,现在入手2500元不到,还等?