参考连接:java
http://blog.csdn.net/hua631150873/article/details/51306021数组
Copy-On-Write简称COW,当要修改某个集合时,进行一份集合拷贝,修改新拷贝后的集合,完成后再将新集合指向旧集合。多线程
查看CopyOnWriteArrayList源码并发
add方法ide
使用final ReentrantLock lock = this.lock;this
lock.lock();.net
在调用add(...)方法前先加锁,保证同一时间只能有一个线程在添加元素。使用Arrays.copyOf(...)方法复制出另外一个新的数组,并且新的数组的长度比原来多1,拷贝完成后,将新添加的元素赋值给新数组,最后把新的副本数组赋值给旧的数组,而后在finally中释放锁。线程
remove方法code
删除元素,主要判断是否是删除最后一个元素,若是是的话,直接复制原来数组的length-1,不然先复制数组index前面到新数组,而后再复制index后面的元素到数组中,最后再把新数组赋值给旧数组的引用。对象
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). Returns the element that was removed from the list. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) // 判断index是否是最后一个,举例若是原来数组总共有3个元素,len 是 3, index 是 2, 则numMoved是0; setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; // 拷贝一个比原来数组长度少一个的数组 System.arraycopy(elements, 0, newElements, 0, index); // 先拷贝index前半部分, System.arraycopy(elements, index + 1, newElements, index, // 再拷贝index+1后面的部分 numMoved); setArray(newElements); // 最后将新数组的引用赋值给旧数组 } return oldValue; } finally { lock.unlock(); } }
其次看System.arraycopy(...)方法的源码
get方法
set方法
代码示例:
多线程异常也不是必定会出现的,但使用ArrayList有很大机会会出现并发异常,下面改用CopyOnWriteArrayList能够解决这个问题。若是使用ArrayList没有出现并发异常,建议把线程池改大点。
package com.jerry.entity; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CopyOnWriteListTest { private static final int THREAD_POOL_MAX_NUM = 10; // private List<String> mList = new ArrayList<String>(); private List<String> mList = new CopyOnWriteArrayList<String>(); public static void main(String[] args) { new CopyOnWriteListTest().start(); } private void initData() { this.mList.add("code_99"); this.mList.add("code_98"); this.mList.add("code_97"); } private void start() { initData(); ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_MAX_NUM); for(int i=1; i<=THREAD_POOL_MAX_NUM; i++) { executorService.execute(new ListReader(this.mList)); String codeId = "code_"+i; executorService.execute(new ListWriter(this.mList, codeId)); } executorService.shutdown(); } private class ListReader implements Runnable { private List<String> rList; public ListReader(List<String> list) { this.rList = list; } @Override public void run() { if(this.rList != null) { System.out.println("Reader Thread: "+Thread.currentThread().getName()+" --> "+this.rList.toString()); } } } private class ListWriter implements Runnable { private List<String> wList; private String codeId; public ListWriter(List<String> list, String codeId) { this.wList = list; this.codeId = codeId; } @Override public void run() { if(this.wList != null) { this.wList.add(codeId); System.out.println("Writer Thread: "+Thread.currentThread().getName()+" --> "+this.wList.toString()); } } } }
CopyOnWriteArrayList的优势和缺点:
优势:解决多线程的并发问题
缺点:
1.内存占用有问题:写时拷贝,内存中会存在两份对象。很明显两个数组同时驻扎在内存中,若是实际应用中,数据比较多,占用内存会比较大,针对这个能够用ConcurrentHashMap来代替。所以,不建议对大对象使用CopyOnWriteArrayList。
ConcurrentHashMap: http://www.javashuo.com/article/p-fzttzvam-gb.html
2. 数据的一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以对实时性要求的数据,不建议使用COW。