- 一、ConcurrentHashMap
- 二、BlockingQueue(暂时忽略)
- 三、ConcurrentLinkedQueue(暂时忽略)
- 四、CopyOnWriteArraylist
- 1. 基本概念
- 2. 源码分析
- 2.1 更改(以JDK8新增为例)
- 2.2 读(JDK8)
- 3. 弱一致性
- 3.1 读操作的弱一致性
- 3.2 迭代器的弱一致性
- 4. 注意
- CopyOnWriteArraylist底层实现采用了写入时拷贝的思想,即增删改操作会将底层数组拷贝一份,在新数组上执行这些操作,这时不影响其它线程的并发读(因为读的是旧数组)。它实现了读写分离。比较适合读多写少的场景。
- CopyOnWriteArraySet是CopyOnWriteArraylist的马甲,它的源码中实际上是定义了一个CopyOnWriteArraylist对象作为其属性,在调用CopyOnWriteArraySet的方法时,底层实际上调用的都是CopyOnWriteArraylist的方法,CopyOnWriteArraySet的存储元素不重复的实现也是通过再添加元素时,调用CopyOnWriteArraylist的addIfAbsent(E e)方法判断内部是否已经存在当前要加入的元素,如果不存在,才进行添加。
加锁是为了保证写写的互斥,不能同时进行写操作。但是读取是不影响的,可以并发执行,只不过读的是旧数组而已。
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
final void setArray(Object[] a) {
array = a;
}
注意:JDK8和JDK11有所不同,不同的是JDK8中使用的是ReentrantLock而JDK11中使用的是synchronized。
2.2 读(JDK8)读操作是未加锁的。
private transient volatile Object[] array;
public E get(int index) {
return get(getArray(), index);
}
final Object[] getArray() {
return array;
}
private E get(Object[] a, int index) {
return (E) a[index];
}
public void forEach(Consumer super E> action) {
Objects.requireNonNull(action);
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
3. 弱一致性
3.1 读操作的弱一致性
读操作指的是foreach遍历操作和get操作,下面仅以get操作为例进行说明。
- Thread-0想要读取CopyOnWriteArraylist中下标为0的数据,获得了CopyOnWriteArraylist内部的属性array的引用,此时,仅仅获取了数组的引用,还未来的及读取数据。
- Thread-1想要对数组下标为0的元素进行删除,复制了一个新的数组,并删除了下标为0的元素,将新的数组的引用设置为CopyOnWriteArraylist内部的属性array的引用。
- 此时尽管下标为0的元素已经删除了,且新的引用已经赋给了CopyOnWriteArraylist内部的属性array,但是Thread-0拿到的还是原来的引用,因此还可以读到原来的下标为0的数据。
再利用迭代器进行遍历的情况下,同样会存在上述的问题。
CopyOnWriteArrayList4. 注意list = new CopyOnWriteArrayList<>(); list.add(1); list.add(2); list.add(3); Iterator iter = list.iterator(); new Thread(() -> { list.remove(0); System.out.println(list); }).start(); sleep1s(); while (iter.hasNext()) { System.out.println(iter.next()); }
- 并不要觉得弱一致性就不好,它可以做到读写的并发,数据库的 MVCC 都是弱一致性的表现。
- 并发高和一致性是矛盾的,需要权衡。



