并发编程工具集——并发容器-下(二十五)

List

  1. List 里面只有一个实现类就是 CopyOnWriteArrayList
  2. CopyOnWrite,写的时候会将共享变量新复制一份出来,读操作完全无锁;适合读多写少的场景,写操作会复制数组,在新的数组中操作
  3. 实现原理:CopyOnWriteArrayList 内部维护了一个数组,成员变量 array 就指向这个内部数组,所有的读操作都是基于 array 进行的,如下图所示,迭代器 Iterator 遍历的就是 array 数组。
    • 如果在遍历 array 的同时,还有一个写操作,例如增加元素,CopyOnWriteArrayList 会将 array 复制一份,然后在新复制处理的数组上执行增加元素的操作,执行完之后再将 array 指向这个新的数组。
    • 通过下图你可以看到,读写是可以并行的,遍历操作一直都是基于原 array 执行,而写操作则是基于新 array 进行。
  4. 注意事项(坑):
    • CopyOnWriteArrayList仅适用于写操作很少的场景,而且能够容忍读写短暂的不一致。
  5. CopyOnWriteArrayList迭代器是只读的,不支持增删改,因为遍历的是一个快照,对快照增删改显然没有用

Map

  1. Map接口实现了ConcurrentHashMap和ConcurrentSkipListMap,从应用角度来看主要区别在于ConcurrentHashMap的key是无序的,ConcurrentSkipListMap的key是有序的;
  2. 他们俩key-value都不能为null,否则报空
  3. ConcurrentSkipListMap 里面的 SkipList 本身就是一种数据结构,被称为跳表,跳表插入、删除、查询操作平均的时间复杂度是 O(log n),理论上和并发线程数没有关系,所以在并发度非常高的情况下,如果对 ConcurrentHashMap 的性能不满意,可以尝试一下 ConcurrentSkipListMap。

Set

  1. Set 接口的两个实现是 CopyOnWriteArraySet 和 ConcurrentSkipListSet,使用场景类似于CopyOnWriteArrayList和ConcurrentSkipListMap,原理都一样就是少了个Value。

 

Queue

  1. 可以从以下两个维度来对Queue分类:一个维度是阻塞与非阻塞;另一个维度是单端与双端
    • 阻塞与非阻塞:所谓阻塞指的是当队列已满时,入队操作阻塞;当队列已空时,出队操作阻塞。
    • 单端与双端:单端指的是只能队尾入队,队首出队;而双端指的是队首队尾皆可入队出队。
  2. Java 并发包里阻塞队列都用 Blocking 关键字标识,单端队列使用 Queue 标识,双端队列使用 Deque 标识。
  3. 这两个维度组合后,可以将 Queue 细分为四大类:单端阻塞队列、双端阻塞队列、单端非阻塞队列、双端非阻塞队列
    • 单端阻塞队列:
      1. 包括:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、LinkedTransferQueue、PriorityBlockingQueue 和 DelayQueue。
      2. 内部一般会持有一个队列,这个队列可以是数组(其实现是 ArrayBlockingQueue)也可以是链表(其实现是 LinkedBlockingQueue);甚至还可以不持有队列(其实现是 SynchronousQueue),此时生产者线程的入队操作必须等待消费者线程的出队操作。而 LinkedTransferQueue 融合 LinkedBlockingQueue 和 SynchronousQueue 的功能,性能比 LinkedBlockingQueue 更好;PriorityBlockingQueue 支持按照优先级出队;DelayQueue 支持延时出队。
    • 双端阻塞队列: 其实现是 LinkedBlockingDeque。
    • 单端非阻塞队列:其实现是 ConcurrentLinkedQueue。
    • 双端非阻塞队列:其实现是 ConcurrentLinkedDeque。

  4. 注意事项:
    • 使用队列时,需要格外注意队列是否支持有界(所谓有界指的是内部的队列是否有容量限制)。
    • 实际工作中,一般都不建议使用无界的队列,因为数据量大了之后很容易导致 OOM。
    • 只有 ArrayBlockingQueue 和 LinkedBlockingQueue 是支持有界的,所以在使用其他无界队列时,一定要充分考虑是否存在导致 OOM 的隐患。
      1. 这里的有界,是指生成队列的时候,在构造函数中指定队列的大小;
      2. 如果不指定,则认为是无界的,实际上,即使调用了无界的构造函数,其队列的大小就是Integer.MAX_VALUE

Fail-Fast(快速失败机制)

  1. 当一个线程遍历容器时,有另一个线程对其修改就会触发fail fast机制,遍历容器的线程也会抛出异常,本质是为了确保遍历时的线程安全,容器内部维护一个初始化为modCount的expectedModeCount变量,遍历时会去检查该期望值是否等于modCount,不等于就说明出现了修改集合操作(可以参考集合框架篇)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值