CopyOnWriteArrayList:是一个并发的线程安全的集合,内部存储结构采用Object[ ]数组,使用ReentrantLock来保证线程安全,允许多线程并发读取,但只允许单线程写入
Copy-On-Write简称cow,是一种利于集合并发访问的优化策略。基本思想是:在集合增删改元素时,并不会直接写入,而是先复制,将元素复制到新容器,再写入,完成之后再将原容器的引用指向新容器。
下面,我们通过给CopyOnWriteArrayList的部分源码加注释来进一步认识这个并发安全的集合 :
首先是CopyOnWriteArrayList的构造方法:
- //无参构造方法:初始化数组
- public CopyOnWriteArrayList() {
- setArray(new Object[0]);
- }
-
- //有参构造方法
- public CopyOnWriteArrayList(Collection extends E> c) {
- Object[] elements;
- //判断传入的集合是否是CopyOnWriteArrayList类
- if (c.getClass() == CopyOnWriteArrayList.class)
- //类型一致,直接存储
- elements = ((CopyOnWriteArrayList>)c).getArray();
- else {//类型不一致
- elements = c.toArray();//将传入的集合转为数组
- // c.toArray might (incorrectly) not return Object[] (see 6260652)
- if (elements.getClass() != Object[].class) //判断数组是否为Object数组
- //不是Object数组,按照Object数组存储
- elements = Arrays.copyOf(elements, elements.length, Object[].class);
- }
- setArray(elements);
- }
- //修改、更新
- public E set(int index, E element) {
- //创建ReentrantLock锁对象
- final ReentrantLock lock = this.lock;
- lock.lock();//加锁
- try {
- Object[] elements = getArray(); //获取数组
- E oldValue = get(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 oldValue;//返回被修改前的值
- } finally {
- lock.unlock();//释放锁
- }
- }
- //添加新元素
- public boolean add(E e) {
- //创建锁对象、加锁
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] elements = getArray();//获取数组
- int len = elements.length;//获取数组长度
- //创建新数组并将元素组复制一份且长度增加1个
- Object[] newElements = Arrays.copyOf(elements, len + 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) //判断传入的参数下标位置是否大于数组长度或者小于0
- throw new IndexOutOfBoundsException("Index: "+index+
- ", Size: "+len); //传入参数的下标不规范,抛异常
- Object[] newElements; //创建新数组
- int numMoved = len - index; //数组长度-传入的下标
- if (numMoved == 0)
- //数组长度-下标=0表示添加元素的位置在数组末尾处
- newElements = Arrays.copyOf(elements, len + 1); //创建新数组,复制原数组,长度+1
- else { //如果添加新元素不在末尾位置
- newElements = new Object[len + 1]; //初始化新数组,长度+1
- System.arraycopy(elements, 0, newElements, 0, index); //复制指定添加位置之前的index个元素到新数组
- System.arraycopy(elements, index, newElements, index + 1,
- numMoved); //从指定添加位置的后一位复制到末尾处至新数组
- }
- newElements[index] = element; //将需添加元素按照指定位置添加至新数组
- setArray(newElements); //将新数组存回去
- } finally {
- lock.unlock();
- }
- }
- 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; //数组长度-删除位置的下表-1
- if (numMoved == 0) //数组长-删除位置下标-1==0 表示删除的是末尾元素
- //删除的是末尾元素,直接重新存入数组长度-1个值到新数组,末尾元素没复制上,就达到删除目的
- setArray(Arrays.copyOf(elements, len - 1));
- else {
- Object[] newElements = new Object[len - 1]; //创建新数组,长度为原数组长度-1
- System.arraycopy(elements, 0, newElements, 0, index);//复制需删除元素位置以前的元素到新数组
- System.arraycopy(elements, index + 1, newElements, index,
- numMoved);//跳过被删除位置,从该位置的后一位开始复制,
- setArray(newElements);//将新数组存入
- }
- return oldValue;
- } finally {
- lock.unlock();
- }
- }
- //按照指定范围删除元素
- void removeRange(int fromIndex, int toIndex) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] elements = getArray();
- int len = elements.length;
- //判断传入的下表是否不符合要求,
- if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
- throw new IndexOutOfBoundsException(); //传值有误,抛异常
- int newlen = len - (toIndex - fromIndex); //获取删除后数组剩余元素个数
- int numMoved = len - toIndex; //数组长度-删除位置的最大值(删除区间的右边)
- if (numMoved == 0) //如果删除区间右边=0,表示删除的区间是数组末尾部分
- setArray(Arrays.copyOf(elements, newlen)); //删除的是数组末尾部分,直接从数组起始部分存入剩余元素个数的值
- else {
- //删除区间不在末尾部分
- Object[] newElements = new Object[newlen]; //创建新数组,长度为剩余元素个数
- System.arraycopy(elements, 0, newElements, 0, fromIndex); //从下表为0复制到删除区间的起始位置
- System.arraycopy(elements, toIndex, newElements,
- fromIndex, numMoved); //从删除区间的结束位置复制到原数组的末尾处,复制删除区间以右的元素个数
- setArray(newElements);
- }
- } finally {
- lock.unlock();
- }
- }
- public boolean retainAll(Collection> c) {
- //集合为空,空指针异常
- if (c == null) throw new NullPointerException();
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] elements = getArray();
- int len = elements.length;
- //如果数组长度不等于0
- if (len != 0) {
- // temp array holds those elements we know we want to keep
- int newlen = 0;
- Object[] temp = new Object[len]; //创建和原数组长度相同的新数组
- for (int i = 0; i < len; ++i) {
- Object element = elements[i];//遍历获取原数组的值
- if (c.contains(element))//判断原数组的值是否存在传入的集合里
- temp[newlen++] = element; //存在,存入新数组
- }
- if (newlen != len) { //如果新数组存入元素的个数不等于数组长度
- setArray(Arrays.copyOf(temp, newlen)); //则将型数组中有元素的返回
- return true;
- }
- }
- return false;
- } finally {
- lock.unlock();
- }
- }
- //清空集合
- public void clear() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- setArray(new Object[0]); //存入一个长度为0的新数组
- } finally {
- lock.unlock();
- }
- }
- //在指定位置添加集合
- public boolean addAll(int index, Collection extends E> c) {
- Object[] cs = c.toArray();//将传入的集合转成数组
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] elements = getArray();//获取原数组
- int len = elements.length; //原数组的长度
- if (index > len || index < 0) //判断传入的下标值是否小于0或大于数组长度
- throw new IndexOutOfBoundsException("Index: "+index+
- ", Size: "+len); //不符合数组下标的规范 ,抛异常
- if (cs.length == 0)//判断插入值的数组长度是否等于0
- return false; //返回false
- int numMoved = len - index; //原数组长度-传入的指定下标位置
- Object[] newElements; //创建新数组的引用
- if (numMoved == 0) //判断数组插入的位置是否为原数组末尾
- //将原数组复制至新数组,新数组长度为原数组长度+插入数组长度
- newElements = Arrays.copyOf(elements, len + cs.length);
- else { //数组插入位置不在原数组末尾处
- newElements = new Object[len + cs.length]; //初始化新数组,长度为原数组长+插入数组长度之和
- System.arraycopy(elements, 0, newElements, 0, index); //将原数组从起始位置至插入位置复制到新数组
- System.arraycopy(elements, index,
- newElements, index + cs.length,
- numMoved); //将原数组从插入位置之后的元素复制到新数组的(插入位置+插入数组长度)的位置
- }//判断结束,无论插入位置在哪,都有如下共同操作步骤
- System.arraycopy(cs, 0, newElements, index, cs.length); //将传入的集合转化的数组全部元素传入新数组的index(传入下标)的位置
- setArray(newElements);//存储新数组
- return true;
- } finally {
- lock.unlock(); //释放锁
- }
- }
- }