• CopyOnWriteArrayList源码分析


    CopyOnWriteArrayList:是一个并发的线程安全的集合,内部存储结构采用Object[ ]数组,使用ReentrantLock来保证线程安全,允许多线程并发读取,但只允许单线程写入

    Copy-On-Write简称cow,是一种利于集合并发访问的优化策略。基本思想是:在集合增删改元素时,并不会直接写入,而是先复制,将元素复制到新容器,再写入,完成之后再将原容器的引用指向新容器。

    下面,我们通过给CopyOnWriteArrayList的部分源码加注释来进一步认识这个并发安全的集合 :

    首先是CopyOnWriteArrayList的构造方法:

    1. //无参构造方法:初始化数组
    2. public CopyOnWriteArrayList() {
    3. setArray(new Object[0]);
    4. }
    5. //有参构造方法
    6. public CopyOnWriteArrayList(Collection c) {
    7. Object[] elements;
    8. //判断传入的集合是否是CopyOnWriteArrayList类
    9. if (c.getClass() == CopyOnWriteArrayList.class)
    10. //类型一致,直接存储
    11. elements = ((CopyOnWriteArrayList)c).getArray();
    12. else {//类型不一致
    13. elements = c.toArray();//将传入的集合转为数组
    14. // c.toArray might (incorrectly) not return Object[] (see 6260652)
    15. if (elements.getClass() != Object[].class) //判断数组是否为Object数组
    16. //不是Object数组,按照Object数组存储
    17. elements = Arrays.copyOf(elements, elements.length, Object[].class);
    18. }
    19. setArray(elements);
    20. }

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

    1. //修改、更新
    2. public E set(int index, E element) {
    3. //创建ReentrantLock锁对象
    4. final ReentrantLock lock = this.lock;
    5. lock.lock();//加锁
    6. try {
    7. Object[] elements = getArray(); //获取数组
    8. E oldValue = get(elements, index);//获取传入的下标对应修改前的值
    9. //判断修改前的值和修改后的值是否相等
    10. if (oldValue != element) {
    11. //不相等:获取数组的长度
    12. int len = elements.length;
    13. //创建新数组,将原数组复制一份
    14. Object[] newElements = Arrays.copyOf(elements, len);
    15. //将新数组的传入下表位置的值修改为传入的第二个参数
    16. newElements[index] = element;
    17. //将新数组存回去
    18. setArray(newElements);
    19. } else {
    20. //修改前的值和要修改的值相等情况:直接将原数组存回去
    21. // Not quite a no-op; ensures volatile write semantics
    22. setArray(elements);
    23. }
    24. return oldValue;//返回被修改前的值
    25. } finally {
    26. lock.unlock();//释放锁
    27. }
    28. }

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

    1. //添加新元素
    2. public boolean add(E e) {
    3. //创建锁对象、加锁
    4. final ReentrantLock lock = this.lock;
    5. lock.lock();
    6. try {
    7. Object[] elements = getArray();//获取数组
    8. int len = elements.length;//获取数组长度
    9. //创建新数组并将元素组复制一份且长度增加1个
    10. Object[] newElements = Arrays.copyOf(elements, len + 1);
    11. //将传入需添加元素加入新数组的末尾位置
    12. newElements[len] = e;
    13. setArray(newElements);//存回新数组
    14. return true;//返回成功
    15. } finally {
    16. lock.unlock();//释放锁
    17. }
    18. }

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

    1. public void add(int index, E element) {
    2. final ReentrantLock lock = this.lock;
    3. lock.lock();
    4. try {
    5. Object[] elements = getArray();//获取数组
    6. int len = elements.length; //获取数组长度
    7. if (index > len || index < 0) //判断传入的参数下标位置是否大于数组长度或者小于0
    8. throw new IndexOutOfBoundsException("Index: "+index+
    9. ", Size: "+len); //传入参数的下标不规范,抛异常
    10. Object[] newElements; //创建新数组
    11. int numMoved = len - index; //数组长度-传入的下标
    12. if (numMoved == 0)
    13. //数组长度-下标=0表示添加元素的位置在数组末尾处
    14. newElements = Arrays.copyOf(elements, len + 1); //创建新数组,复制原数组,长度+1
    15. else { //如果添加新元素不在末尾位置
    16. newElements = new Object[len + 1]; //初始化新数组,长度+1
    17. System.arraycopy(elements, 0, newElements, 0, index); //复制指定添加位置之前的index个元素到新数组
    18. System.arraycopy(elements, index, newElements, index + 1,
    19. numMoved); //从指定添加位置的后一位复制到末尾处至新数组
    20. }
    21. newElements[index] = element; //将需添加元素按照指定位置添加至新数组
    22. setArray(newElements); //将新数组存回去
    23. } finally {
    24. lock.unlock();
    25. }
    26. }

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

    1. public E remove(int index) {
    2. final ReentrantLock lock = this.lock;
    3. lock.lock();
    4. try {
    5. Object[] elements = getArray();
    6. int len = elements.length;//获取数组长度
    7. E oldValue = get(elements, index); //根据下标获取需删除位置的值
    8. int numMoved = len - index - 1; //数组长度-删除位置的下表-1
    9. if (numMoved == 0) //数组长-删除位置下标-1==0 表示删除的是末尾元素
    10. //删除的是末尾元素,直接重新存入数组长度-1个值到新数组,末尾元素没复制上,就达到删除目的
    11. setArray(Arrays.copyOf(elements, len - 1));
    12. else {
    13. Object[] newElements = new Object[len - 1]; //创建新数组,长度为原数组长度-1
    14. System.arraycopy(elements, 0, newElements, 0, index);//复制需删除元素位置以前的元素到新数组
    15. System.arraycopy(elements, index + 1, newElements, index,
    16. numMoved);//跳过被删除位置,从该位置的后一位开始复制,
    17. setArray(newElements);//将新数组存入
    18. }
    19. return oldValue;
    20. } finally {
    21. lock.unlock();
    22. }
    23. }

    按照指定范围删除元素 

    1. //按照指定范围删除元素
    2. void removeRange(int fromIndex, int toIndex) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. Object[] elements = getArray();
    7. int len = elements.length;
    8. //判断传入的下表是否不符合要求,
    9. if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
    10. throw new IndexOutOfBoundsException(); //传值有误,抛异常
    11. int newlen = len - (toIndex - fromIndex); //获取删除后数组剩余元素个数
    12. int numMoved = len - toIndex; //数组长度-删除位置的最大值(删除区间的右边)
    13. if (numMoved == 0) //如果删除区间右边=0,表示删除的区间是数组末尾部分
    14. setArray(Arrays.copyOf(elements, newlen)); //删除的是数组末尾部分,直接从数组起始部分存入剩余元素个数的值
    15. else {
    16. //删除区间不在末尾部分
    17. Object[] newElements = new Object[newlen]; //创建新数组,长度为剩余元素个数
    18. System.arraycopy(elements, 0, newElements, 0, fromIndex); //从下表为0复制到删除区间的起始位置
    19. System.arraycopy(elements, toIndex, newElements,
    20. fromIndex, numMoved); //从删除区间的结束位置复制到原数组的末尾处,复制删除区间以右的元素个数
    21. setArray(newElements);
    22. }
    23. } finally {
    24. lock.unlock();
    25. }
    26. }

    保留与传入数组的交集的结果数组

    1. public boolean retainAll(Collection c) {
    2. //集合为空,空指针异常
    3. if (c == null) throw new NullPointerException();
    4. final ReentrantLock lock = this.lock;
    5. lock.lock();
    6. try {
    7. Object[] elements = getArray();
    8. int len = elements.length;
    9. //如果数组长度不等于0
    10. if (len != 0) {
    11. // temp array holds those elements we know we want to keep
    12. int newlen = 0;
    13. Object[] temp = new Object[len]; //创建和原数组长度相同的新数组
    14. for (int i = 0; i < len; ++i) {
    15. Object element = elements[i];//遍历获取原数组的值
    16. if (c.contains(element))//判断原数组的值是否存在传入的集合里
    17. temp[newlen++] = element; //存在,存入新数组
    18. }
    19. if (newlen != len) { //如果新数组存入元素的个数不等于数组长度
    20. setArray(Arrays.copyOf(temp, newlen)); //则将型数组中有元素的返回
    21. return true;
    22. }
    23. }
    24. return false;
    25. } finally {
    26. lock.unlock();
    27. }
    28. }

    清空集合

    1. //清空集合
    2. public void clear() {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. setArray(new Object[0]); //存入一个长度为0的新数组
    7. } finally {
    8. lock.unlock();
    9. }
    10. }

    在原集合指定位置添加集合

    1. //在指定位置添加集合
    2. public boolean addAll(int index, Collection c) {
    3. Object[] cs = c.toArray();//将传入的集合转成数组
    4. final ReentrantLock lock = this.lock;
    5. lock.lock();
    6. try {
    7. Object[] elements = getArray();//获取原数组
    8. int len = elements.length; //原数组的长度
    9. if (index > len || index < 0) //判断传入的下标值是否小于0或大于数组长度
    10. throw new IndexOutOfBoundsException("Index: "+index+
    11. ", Size: "+len); //不符合数组下标的规范 ,抛异常
    12. if (cs.length == 0)//判断插入值的数组长度是否等于0
    13. return false; //返回false
    14. int numMoved = len - index; //原数组长度-传入的指定下标位置
    15. Object[] newElements; //创建新数组的引用
    16. if (numMoved == 0) //判断数组插入的位置是否为原数组末尾
    17. //将原数组复制至新数组,新数组长度为原数组长度+插入数组长度
    18. newElements = Arrays.copyOf(elements, len + cs.length);
    19. else { //数组插入位置不在原数组末尾处
    20. newElements = new Object[len + cs.length]; //初始化新数组,长度为原数组长+插入数组长度之和
    21. System.arraycopy(elements, 0, newElements, 0, index); //将原数组从起始位置至插入位置复制到新数组
    22. System.arraycopy(elements, index,
    23. newElements, index + cs.length,
    24. numMoved); //将原数组从插入位置之后的元素复制到新数组的(插入位置+插入数组长度)的位置
    25. }//判断结束,无论插入位置在哪,都有如下共同操作步骤
    26. System.arraycopy(cs, 0, newElements, index, cs.length); //将传入的集合转化的数组全部元素传入新数组的index(传入下标)的位置
    27. setArray(newElements);//存储新数组
    28. return true;
    29. } finally {
    30. lock.unlock(); //释放锁
    31. }
    32. }
    33. }

  • 相关阅读:
    HTTP的长连接(使用selectPage分页获取总页数处理)
    八、cadence ic 5141 ——反相器原理图验证
    带你了解一下PHP搭建的电商商城系统
    django_model_一对一映射
    DDE图像增强
    微信小程序获取openid流程
    江坪水利枢纽工程施工组织设计——碾压混凝土围堰
    进程调度例题解析
    GLM-4-9B 支持 Ollama 部署
    2022钉钉杯初赛A题(银行卡电信诈骗危险预测)
  • 原文地址:https://blog.csdn.net/weixin_53233753/article/details/126913022