CopyOnWriteArrayList源码分析

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);
    }

根据指定下标位置修改值 set(下标,修改值)

//修改、更新
    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();//释放锁
        }
    }

 添加新元素值  add(添加元素的对象)

//添加新元素
    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();//释放锁
        }
    }

 在指定位置添加新元素 add(下标,添加元素的对象)

    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();
        }
    }

按照指定下标删除元素 remove(下标值)

 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(); //释放锁
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值