CopyOnWriteArrayList源码分析

概述

        CopyOnWriteArrayList是一个线程安全的集合,当我们对集合容器使用增删改操作时,并不是直接对方法进行加锁,在原集合进行操作,而是通过复制出一个新集合,然后在新集合中进行操作,操作结束后,将原集合容器的引用指向新的集合容器,所以他能实现写入时的线程安全,但不影响并发的读取操作,所以适用于读多写少的场景,是一种读写分离的思想。

        但是由于每个操作都需要拷贝原集合,数据较大的时候容易引起频繁Full GC;而且写和读在不同的Object[]数组上,容易引起数据不一致的问题。

源码

构造方法

    final transient ReentrantLock lock = new ReentrantLock();

    private transient volatile Object[] array;
	
	// 返回Object类型的数组
    final Object[] getArray() {
        return array;
    }
	
	// 更新当前集合的数组
    final void setArray(Object[] a) {
        array = a;
    }


   
    public CopyOnWriteArrayList() {
		// 设置当前集合数组为0
		setArray(new Object[0]);
    }


    public CopyOnWriteArrayList(Collection 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)
			// 判断当前对象的class是否是Object
            if (elements.getClass() != Object[].class)
				// 当前集合复制成Object类型
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
		// 更新集合数组
        setArray(elements);
    }

 set(int index, E element):修改指定下标的值

// 修改指定下标的值
    public E set(int index, E element) {
		// 加锁
        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(E e):添加新元素

// 添加新元素
    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(int index, E element):添加元素至指定位置

// 添加元素至指定位置
    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)
				// 直接复制原数组长度+1
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
				// 从0开始复制index个元素
                System.arraycopy(elements, 0, newElements, 0, index);
                // 从index下标开始复制numMoved个元素
				System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
			// 新数组指定下标赋值
            newElements[index] = element;
            setArray(newElements);
        } finally {
			// 解锁
            lock.unlock();
        }
    }

remove(int index):删除指定下标的元素

// 删除指定下标的元素
    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)
				// 直接复制原数组长度-1
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
				// 从0开始复制index个元素
                System.arraycopy(elements, 0, newElements, 0, index);
				 // 原数组从index+1下标开始,新数组从index开始,复制numMoved个元素
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
			// 解锁
            lock.unlock();
        }
    }

removeRange(int fromIndex, int toIndex):删除指定下标区间的元素

// 删除指定下标区间的元素
    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)
				// 复制newlen长度
                setArray(Arrays.copyOf(elements, newlen));
            else {
                Object[] newElements = new Object[newlen];
				// 从0开始复制fromIndex长度
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
				// 原数组从toIndex下标开始,新数组从fromIndex开始复制numMoved长度
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);
            }
        } finally {
			// 解锁
            lock.unlock();
        }
    }

retainAll(Collection c):判断集合间是否有交集并把原集合更新成交集

// 判断集合间是否有交集并把原集合更新成交集
    public boolean retainAll(Collection c) {
		// 如果集合为空,抛出NullPointerException()异常
        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();
        }
    }

clear():清空集合

// 清空集合
    public void clear() {
		// 加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
			// 设置数组为空数组
            setArray(new Object[0]);
        } finally {
			// 解锁
            lock.unlock();
        }
    }

 addAll(int index, Collection c): 在原集合指定位置添加新集合

 // 在原集合指定位置添加新集合
    public boolean addAll(int index, Collection 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)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            // 判断新集合是否为空
			if (cs.length == 0)
                return false;
            int numMoved = len - index;
            Object[] newElements;
			// 判断指定下标是否是尾下标
            if (numMoved == 0)
				// 复制新数组并且长度是原数组长度+新数组长度
                newElements = Arrays.copyOf(elements, len + cs.length);
            else {
                newElements = new Object[len + cs.length];
				// 从0开始复制index个元素
				System.arraycopy(elements, 0, newElements, 0, index);
				// 从原集合从index下标开始,结果集合从index+新数组长度开始复制numMoved个元素
                System.arraycopy(elements, index,
                                 newElements, index + cs.length,
                                 numMoved);
            }
			// 新数组从0开始,结果数组从index开始,复制新数组长度
            System.arraycopy(cs, 0, newElements, index, cs.length);
			// 设置数组为结果数组
            setArray(newElements);
            return true;
        } finally {
			// 解锁
            lock.unlock();
        }
    }

你可能感兴趣的:(java,jvm,COW,ReentrantLock)