标签:blog io ar os 使用 java for sp strong
java.util.concurrent包中定义常见集合类对应的并发集合类,用于高效处理并发场景,其中CopyOnWriteArrayList对应就是ArrayList。顾名思义CopyOnWrite,写时拷贝,这里写包括对集合类的修改操作,都会创建一个副本。
类的定义
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable可以看到没有继承任何子类,实现接口和ArrayList类似。
关键属性
/** The lock protecting all mutators */ transient final ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private volatile transient Object[] array;同样是采用数组方式实现,多了一个volatile声明,用于保证线程可见性。没有size声明表示实际包含元素的大小,多了一个ReentrantLock对象声明。
常见方法
构造方法
public CopyOnWriteArrayList() { setArray(new Object[0]); //默认创建一个空数组 } public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements = c.toArray(); // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class);//拷贝一份数组 setArray(elements); }size方法,直接返回数组大小,说明array数组只包含实际大小的空间
public int size() { return getArray().length; }get方法,和ArrayList中类似,不过没有index的范围判断
public E get(int index) { return (E)(getArray()[index]); }add方法,可以看到无论是在尾部还是指定位置添加,都有锁定和解锁操作,在设置值之前都先将原先数组拷贝一份并扩容至size+1大小。
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);//拷贝array属性,并扩展为length+1大小 newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); //解锁 } } public void add(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+len); Object[] newElements; int numMoved = len - index; if (numMoved == 0) //尾部添加 newElements = Arrays.copyOf(elements, len + 1); else { newElements = new Object[len + 1]; //elements[0,index) ---> newElements[0,index) System.arraycopy(elements, 0, newElements, 0, index); //elements[index,len) --> newElements[index+1,len+1) System.arraycopy(elements, index, newElements, index + 1, numMoved); } newElements[index] = element; setArray(newElements); } finally { lock.unlock(); } }set方法,ArrayList中set方法直接改变数组中对应的引用,这里需要拷贝数组然后再设置。(else那个分支没看懂,为什么值没有改变还需要设置来保证volatile写语义)
public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); Object oldValue = elements[index]; if (oldValue != element) { int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return (E)oldValue; } finally { lock.unlock(); } }remove(int)方法,和指定位置添加类似,需要拷贝[0,index)和[index+1,len)之间的元素
public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object oldValue = elements[index]; int numMoved = len - index - 1;nt if (numMoved == 0) //删除最后一个元素 setArray(Arrays.copyOf(elements, len - 1)); else { Object[] newElements = new Object[len - 1]; //elements[0,index) --> newElements[0,index) System.arraycopy(elements, 0, newElements, 0, index); //elements[index+1,len) --> newElements[index,len-1) System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return (E)oldValue; } finally { lock.unlock(); } }remove(Object)方法,分配一个len-1大小的新数组,遍历原来数组,如果找到则将原来数组以后的元素拷贝到新数组中并将list设置为新数组,否则直接给新数组赋值上原来数组。
public boolean remove(Object o) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; if (len != 0) { // Copy while searching for element to remove // This wins in the normal case of element being present int newlen = len - 1; Object[] newElements = new Object[newlen]; for (int i = 0; i < newlen; ++i) { if (eq(o, elements[i])) { // found one; copy remaining and exit for (int k = i + 1; k < len; ++k) newElements[k-1] = elements[k]; setArray(newElements); return true; } else newElements[i] = elements[i]; } // special handling for last cell if (eq(o, elements[newlen])) { setArray(newElements); return true; } } return false; } finally { lock.unlock(); } }
COWIterator定义
/** Snapshot of the array **/ private final Object[] snapshot; /** Index of element to be returned by subsequent call to next. */ private int cursor;
构造器
private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; }iterator和listIterator中会传递当前数组的引用和cursor(无参方法为0,有参数方法为对应值)
常见方法
public boolean hasNext() { return cursor < snapshot.length; } public boolean hasPrevious() { return cursor > 0; } public E next() { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; } public E previous() { if (! hasPrevious()) throw new NoSuchElementException(); return (E) snapshot[--cursor]; }另外其他add、remove和set修改容器的方法都没有实现,直接throw new UnsupportedOperationException();
1. CopyOnWriteArrayList的迭代器保留一个执行底层基础数组的引用,这个数组当前位于迭代器的起始位置,由于基础数组不会被修改(修改都是复制一个新的数组),因此对其同步只需要保证数组内容的可见性。多个线程可以同时对这个容器进行迭代,而不会彼此干扰或者与修改容器的线程互相干扰。不会抛出CocurrentModificationException,并且返回元素与创建迭代器创建时的元素完全一致,不必考虑之后修改操作带来影响。
2. 每次修改容器都会复制底层数组,这需要一定开销,特别是容器规模较大。仅当迭代操作远远多于修改操作时,才应该使用CopyOnWriteArrayList。
标签:blog io ar os 使用 java for sp strong
原文地址:http://blog.csdn.net/changer328/article/details/40711781