1、原理
CopyOnWriteArrayList是一个线程安全的ArrayList
如果一段并发程序,读操作明显多于写操作的话,那么使用CopyOnWriteArrayList的性能会比Vector更高
CopyOnWriteArrayList的实现原理就是读写分离,它对所有的写操作都使用ReentrantLock来加锁,对所有的读操作都不加锁,那它是怎么保证线程安全性问题的呢?
CopyOnWriteArrayList在写操作的时候,都会将list中的数组copy一份作为缓存,然后对该缓存中的数组进行操作(此时若有其他线程过来读的话,那么该线程读的还是原先没有被修改过的数组,若有其他线程过来写的话,那么该线程会因为ReentrantLock的原因被锁在外面。),操作完毕后再将list中的数组地址引用指向修改后的新数组地址。
由CopyOnWriteArrayList的原理我们可以看出,我们每次往list里面写数据的时候,数组都需要重新copy一份,所以CopyOnWriteArrayList不需要实现像ArrayList一样的扩容机制,初始创建时让list中的数组长度为0,我们每次add元素的时候,只需要对新数组长度进行加1操作即可,所以CopyOnWriteArrayList实现起来相对还是比ArrayList简单的。
// 构造方法 public CopyOnWriteArrayList() { setArray(new Object[0]); } // 添加一个元素 public boolean add(E e) { // 加锁 final ReentrantLock lock = this.lock; lock.lock(); try { // 创建一个新数组,长度+1 Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); // 将元素添加到新数组的末端,并将新数组赋值给本对象中的array newElements[len] = e; setArray(newElements); return true; } finally { // 解锁 lock.unlock(); } } // 获取一个元素,没加锁 public E get(int index) { return get(getArray(), index); }
所以我们可以看出,如果是读操作十分频繁的话,那么多线程下使用CopyOnWriteArrayList的性能基本上跟ArrayList差不多了。但如果是写操作十分频繁的话,建议还是不要使用CopyOnWriteArrayList了,因为它会造成数组的不断扩容及复制,十分耗性能。这其实就跟我们数据库读写分离的原理是一样的,如果写操作很多的话,那么主从库就会不断的执行复制操作,消耗性能。但如果是读操作多的话,由于该库只用于读,所以不会发生数据库事务锁,效率就会比一般的单库查询快很多。
2、使用
- CopyOnWriteArrayList的使用和ArrayList差不多,这里没什么好说的