CopyOnWriteArrayList核心源码阅读

目录

底层代码基本的实现原理:

无参构造方法

set(int index, E element)---修改元素

add()---添加元素

remove---删除元素  

clear()---清空 

addAll()--- 添加集合

 sort()---排序

equals()---比较

 hashCode()---计算哈希值


ArrayList我们都知道是List接口下的一个集合,那CopyOnWriteArrayList就是线程安全的ArrayList。它的使用和ArrayList完全相同,那它是怎么实现线程安全的呢?

底层代码基本的实现原理:

当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入, 而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。

 CopyOnWriteArrayList类是线程安全的,使用ReentrantLock加锁,底层数组采用Object类型的数组, volatile关键字可以保证变量的内存可见性。当一个变量被声明为volatile时,编译器和处理器会注意到这个变量可能会被其他线程并发地访问。这样可以避免线程之间的数据竞争,并确保多线程环境下变量的值是最新的。

// CopyOnWriteArrayList类实现了List<E>, RandomAccess, Cloneable, java.io.Serializable这几个接口
public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    // 加锁(ReentrantLock可重入锁)
    final transient ReentrantLock lock = new ReentrantLock();

    // 设置为线程可见数组,用volatile修饰 
    private transient volatile Object[] array;

 获取数组的方法,返回数组对象。

设置数组的方法,参数是一个数组a,把原数组的引用指向a

// 获取数组
final Object[] getArray() {
        return array;
}
// 设置数组,将原数组的引用指向a
final void setArray(Object[] a) {
        array = a;
}

无参构造方法

无参构造方法初始化一个长度为0的数组。

public CopyOnWriteArrayList() {
        // 创建长度为0的数组
        setArray(new Object[0]);
}

 从array数组中根据下标读取值。

private E get(Object[] a, int index) {
        return (E) a[index];
}
    
public E get(int index) {
        return get(getArray(), index);
}

set(int index, E element)---修改元素

当修改数组的某个元素时,会将当前数组Copy复制新数组,在新数组中修改完后,最后替换原数组。整个过程加锁,保证线程的安全

    // 修改指定下标的值
    public E set(int index, E element) {
        // 加锁,保证了在修改的过程中其他线程只允许做读的操作。
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 获取本地数组
            Object[] elements = getArray();
            // 获取本地数组index下标位置原来的值
            E oldValue = get(elements, index);
            // 判断原值与新值是否相同。
            if (oldValue != element) {
                // 如果原值与新值不同
                // 先保存原来数组的长度
                int len = elements.length;
                // 拷贝一份原数组放入newElement中作为新数组
                Object[] newElements = Arrays.copyOf(elements, len);
                // 将新值赋给新数组对应的下标
                newElements[index] = element;
                // 将原数组的引用指向newElement
                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);
            // 将要添加的值放入新数组的len下标的位置
            newElements[len] = e;
            // 将当前数组的引用指向新数组
            setArray(newElements);
            // 添加完成返回true
            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 {
                // 设置新数组长度为当前数组长度加1
                newElements = new Object[len + 1];
                // 拷贝前半部分
                // 从原数组的下标0开始复制index个元素到新数组的下标0开始粘贴
                System.arraycopy(elements, 0, newElements, 0, index);
                // 拷贝后半部分
                // 从原数组的下标index开始复制numMoved个元素到新数组的下标index+1开始粘贴
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            // 将要添加元素的值放入新数组下标index位置
            newElements[index] = element;
            // 将当前数组的引用指向新数组
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

remove---删除元素  

1.删除指定位置上的元素
2.删除指定区间的元素
3.删除集合中元素,首先拷贝一个新数组,判断删除的元素是否在末尾

若是末尾直接替换为原数组

若单个元素,数组长度加1;若为多个元素,数组长度减相应的长度。

若不是末尾元素则将分段拷贝成新数组

最终将替换为原数组

以上操作都是在加锁的情况下进行,保证了线程安全

    public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            // 获取原数组index下标位置的元素值
            E oldValue = get(elements, index);
            // 获取原数组后半部分元素个数减1
            int numMoved = len - index - 1;
            // 如果后半部分没有元素
            if (numMoved == 0)
                // 从原数组0下标开始拷贝len-1个元素,然后让当前数组引用指向新数组
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 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);
            // 计算toIndex下标后的元素个数
            int numMoved = len - toIndex;
            // 如果toIndex下标后没有元素
            if (numMoved == 0)
                // 拷贝原数组newlen前的元素,当前数组引用指向新数组
                setArray(Arrays.copyOf(elements, newlen));
            else {
                Object[] newElements = new Object[newlen];
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);
            }
        } finally {
            lock.unlock();
        }
    }


    public boolean removeAll(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;
                // 创建一个长度为len的空数组
                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()---清空 

初始化为长度0的数组,该过程加锁,保证线程的安全

    public void clear() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            setArray(new Object[0]);
        } finally {
            lock.unlock();
        }
    }

addAll()--- 添加集合

替换为原数组(数组长度+添加集合长度)

以上操作都是在加锁的情况下进行,保证了线程安全

    public boolean addAll(Collection<? extends E> c) {
        // 判断当前集合的类型与CopyOnWriteArrayList的类型是否相同,
        // 如果相同将c强制类型转换,将集合转换为数组,存入cs中
        // 如果不相同直接toArray转换成数组,存入cs中
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            // 如果原数组的长度为0,并且当前数组的类型是Object类型
            // 将cs写入原数组
            if (len == 0 && cs.getClass() == Object[].class)
                setArray(cs);
            else {
                // 如果原数组长度不为0
                // 创建一个新数组,拷贝了原数组,长度是原数组长度加当前数组长度
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                System.arraycopy(cs, 0, newElements, len, cs.length);
                // 当前数组引用指向新数组
                setArray(newElements);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

 sort()---排序

es数组和newElements数组的引用指向相同的内存地址

public void sort(Comparator<? super E> c) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            // 创建新数组
            Object[] newElements = Arrays.copyOf(elements, elements.length);
            @SuppressWarnings("unchecked") E[] es = (E[])newElements;
            // 直接用Arrays工具类中的sort方法,用comparator比较器进行排序
            Arrays.sort(es, c);
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

equals()---比较

 用私有方法eq进行比较

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;
        // 将o转换为List<>类型
        List<?> list = (List<?>)(o);
        // 创建迭代器
        Iterator<?> it = list.iterator();
        // 获取原数组
        Object[] elements = getArray();
        // 获取原数组长度
        int len = elements.length;
        // 遍历原数组
        for (int i = 0; i < len; ++i)
            // 如果it没有下一个元素或者eq方法返回值为false
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        // 如果it有下一个元素,返回false
        if (it.hasNext())
            return false;
        return true;
    }

private static boolean eq(Object o1, Object o2) {
    		//如果o1对象为null,则o2对象为null返回ture,不为null返回false;                           
            //如果o1对象不为 null,则o1和o2内存地址相等返回true
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }

 hashCode()---计算哈希值

hashCode的值变为31*hashCode+元素不为空时的哈希值(调用Object类中的hashCode())

    public int hashCode() {
        // 初始化hashcode,值为1
        int hashCode = 1;
        // 获取原数组
        Object[] elements = getArray();
        // 获取原数组长度
        int len = elements.length;
        // 遍历原数组
        for (int i = 0; i < len; ++i) {
            // 将遍历到的元素的值赋值给obj对象
            Object obj = elements[i];
            // hashCode的值变为31*hashCode+元素不为空时的哈希值(调用Object类中的hashCode())
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
        return hashCode;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值