0%

Java集合类-CopyOnWriteArrayList

ArrayList存在什么问题?

ArrayList是线程不安全的。

可以用Collections.synchronizedList()加全局独占锁保证线程安全,但在读多写少的情况下,多线程读之间互斥降低了系统吞吐量。

如果读与读之间不互斥,写与写、写与读才互斥,这样可以保证最大的吞吐量,这就是CopyOnWriteArrayList。

CopyOnWriteArrayList保证线程安全的原理?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
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();
}
}


public E get(int index) {
return get(getArray(), index);
}

如果用读写锁保证并发,可以保证读读之间没有阻塞等待,但有线程写数据时,读线程必须阻塞等待,这保证了每次都都可以读到最新修改的值。

CopyOnWriteArrayList牺牲了读的实时性,读操作不上锁,只在写数据时使用ReentrantLock上锁,写数据时会把原数组拷贝到新的数组中,写数据的同时再有线程读数据,读到的是之前数组的值,不保证数据实时性,只保证最终结果一致性,牺牲了数据实时性,换取了读操作没有阻塞等待。

对数据实时性要求很高的需求,不要使用CopyOnWriteArrayList。

线程1在add数据时,线程2随后get数据,不一定能获取到线程1刚add的的元素,因为线程1可能还没执行完。

CopyOnWriteArrayList存在什么问题?

  1. 读数据不是实时的,读的时候可能正在写数据,读不到最新的值,也不会阻塞等待,适合读多写少的场景
  2. 每次写数据都要复制整个数组,如果写操作频繁,会频繁触发垃圾回收,垃圾回收又会导致线程停顿,造成APP卡顿变多

CopyOnWriteArrayList适用场景?

  1. 高并发
  2. 读多写少
  3. 对读数据实时性要求不高

为什么没有扩容?

因为每次add元素都要拷贝数组,这个时间消耗是必须的,所以也没必要扩容了,拷贝数据到一个恰好比原数组多一个位置的新数组。

Vector有什么问题?

  1. Vector是线程安全的列表,底层实现也是数组,但是几乎每一个方法都加上了synchonized,多线程读操作之间会互斥,读多写少的情况下,吞吐量不高
  2. Vector扩容,容量是翻倍,指数增长,ArrayList只增长为原来的1.5倍,更节约空间。