1 - 并发容器

1.1 - 分类

image.png

1.2 - List

1.2.1 - CopyOnWriteArrayList

CopyOnWrite含义: 写的时候会将共享变量复制一份. 这样做的好处是读的时候是完全无锁的.

实现原理:

  1. 内部维护一个数组array. 所有的读操作都是基于这个array进行
  2. 当有写操作时, 加一把ReentrantLock, 然后把array复制一份构造出一个新数组, 再让array指向这个新数组. 而此时进行的读操作, 是基于原先的array, 所以读操作时无锁的.

image.png

需要注意的是:

  1. 应用场景: 适用于读多写少的场景, 而且能够容忍读写的短暂不一致
    1. array字段是volatile修饰, 根据happens-before原则. 当array指向新数组后, 后续读的线程都会读到array最新的数据.
    2. 写操作包含原数组的复制, 如果此时有并发读写, 在写线程还没有执行setArray()方法时发生了线程切换,
  2. 迭代器是只读, 遍历时无法进行增删改操作. 迭代器遍历的仅仅是数据的快照.

image.png

1.3 - Map

Map接口的两个实现是ConcurrentHashMap和ConcurrentSkipListMap.

  1. 前者的key是无序的, 后者key是有序
  2. 两者的key和value都不允许是null

我们知道, HashMap在多线程环境下会导致死循环, CPU飙升. 如果仍然需要在多线程环境下使用map结构,通常有三种办法:

  1. Hashtable. 线程安全类, 几乎所有的方法都添加了synchronized方法
  2. Collections.synchronizedMap: 线程安全类, 对方法添加synchronized方法
  3. ConcurrentHashMap.

Hashtable和Collections.synchronizedMap的方法中都添加了synchronized关键字, 相当于对整个hash表加了一把全局锁, 对元素的所有操作都需要竞争同一把锁, 在多线程竞争激烈的情况下性能会很差.

HashMap为什么不安全: https://mp.weixin.qq.com/s/yxn47A4UcsrORoDJyREEuQ

1.3.1 - ConcurrentHashMap

1.3.2.1 - JDK7中的实现

JDK7中ConcurrentHashMap使用分段锁的思想.

  1. 将HashMap中的数组分成多个小数组
  2. 每个小数组中又有多个HashEntry(冲突链表)
  3. 小数组叫做Segment, 继承自ReentrantLock

put操作:

  1. 计算key的hash值, 并以此来确定目标segment
  2. 获取到segment之后, 获取独占锁
    1. 根据key的hash

1.3.2.2 - JDK8中的实现

在JDK8中, 取消了ConcurrentHashMap的Segment分段锁的设计. 转而使用cas + synchronized来实现.

参考: https://mp.weixin.qq.com/s/CH3gTbf55Cuabstfn2lpFg

1.3.2 - ConcurrentSkipListMap


1.4 - Queue

2 - 并发框架