• CopyOnWriteArrayList源码分析


    目录

    概述

    基本方法以及源码分析

    无参构造方法

    有参构造方法

    set(int index,E element)

    add(E e)添加元素至尾部

    add(int index, E element)在指定下标添加元素

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

    removeRange(int fromIndex, int toIndex)删除指定范围的元素

    clear()清空集合

    addAll(int index, Collection c)在指定位置批量添加元素


    概述

    CopyOnWriteArrayList是一种线程安全的集合,通过ReentrantLock来实现集合的安全性。当我们对集合进行写入操作时,并不会直接在集合中写入,而是先将当前的集合复制,复制出一个新的集合,在新的集合中进行写入操作,写入 完成后,将原集合的引用指向新的集合。

    CopyOnWriteArrayList的设计思想是读写分离,在保证写入操作的安全性时,也不会影响并发读取操作。它的内部使用Object[]数组,允许多线程并发读取,但只有一个线程可以进行写入操作。

    基本方法以及源码分析

    无参构造方法

    1. //默认的构造方法,数组初始化
    2. public CopyOnWriteArrayList() {
    3. setArray(new Object[0]);
    4. }

    有参构造方法

    1. // 有参构造方法
    2. public CopyOnWriteArrayList(Collection c) {
    3. Object[] elements;
    4. // copy的集合与当前集合是同一个Class对象
    5. if (c.getClass() == CopyOnWriteArrayList.class)
    6. // 将当前集合强转为CopyOnWriteArrayList并转为数组,存入elements中
    7. elements = ((CopyOnWriteArrayList)c).getArray();
    8. else {
    9. // 直接将当前集合c赋给elements
    10. elements = c.toArray();
    11. // c.toArray might (incorrectly) not return Object[] (see 6260652)
    12. // 数组类型不是Object[]类型,对数组进行复制操作
    13. if (elements.getClass() != Object[].class)
    14. elements = Arrays.copyOf(elements, elements.length, Object[].class);
    15. }
    16. setArray(elements);
    17. }

    set(int index,E element)

    1. // 修改指定下标的值
    2. public E set(int index, E element) {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 得到原数组
    8. Object[] elements = getArray();
    9. // 得到修改前元素与下标位置
    10. E oldValue = get(elements, index);
    11. // 修改前的元素不等于要修改的元素
    12. if (oldValue != element) {
    13. int len = elements.length;
    14. // 将原来数组的内容与长度复制到新的数组
    15. Object[] newElements = Arrays.copyOf(elements, len);
    16. // 根据元素下标 用新元素替换旧元素
    17. newElements[index] = element;
    18. // 保存新数组(旧数组=>新数组)
    19. setArray(newElements);
    20. } else {
    21. // Not quite a no-op; ensures volatile write semantics
    22. // 修改前后元素是同一个,返回原数组
    23. setArray(elements);
    24. }
    25. return oldValue;
    26. } finally {
    27. // 释放锁
    28. lock.unlock();
    29. }
    30. }

    add(E e)添加元素至尾部

    1. // 添加元素至集合尾部
    2. public boolean add(E e) {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 得到原数组
    8. Object[] elements = getArray();
    9. int len = elements.length;
    10. // 添加操作复制原数组内容的同时长度+1
    11. Object[] newElements = Arrays.copyOf(elements, len + 1);
    12. // 元素添加至数组尾部
    13. newElements[len] = e;
    14. // 保存新数组(旧数组=>新数组)
    15. setArray(newElements);
    16. return true;
    17. } finally {
    18. // 释放锁
    19. lock.unlock();
    20. }
    21. }

    add(int index, E element)在指定下标添加元素

    1. // 在指定下标添加元素
    2. public void add(int index, E element) {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 得到原数组
    8. Object[] elements = getArray();
    9. int len = elements.length;
    10. // 判断下标位置是否合理,不合理抛异常
    11. if (index > len || index < 0)
    12. throw new IndexOutOfBoundsException("Index: "+index+
    13. ", Size: "+len);
    14. Object[] newElements;
    15. int numMoved = len - index;
    16. // 添加的元素的下标是尾下标
    17. if (numMoved == 0)
    18. // 复制原数组,长度加+1
    19. newElements = Arrays.copyOf(elements, len + 1);
    20. else {
    21. // 创建一个新数组,新数组长度=原数组长度+1
    22. newElements = new Object[len + 1];
    23. // 分别复制要添加的位置的前后的数组
    24. System.arraycopy(elements, 0, newElements, 0, index);
    25. System.arraycopy(elements, index, newElements, index + 1,
    26. numMoved);
    27. }
    28. // 将要添加的元素加到新数组的空位置
    29. newElements[index] = element;
    30. setArray(newElements);
    31. } finally {
    32. // 释放锁
    33. lock.unlock();
    34. }
    35. }

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

    1. // 删除指定下标的元素
    2. public E remove(int index) {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 得到当前数组
    8. Object[] elements = getArray();
    9. int len = elements.length;
    10. // 获取要删除的元素和下标
    11. E oldValue = get(elements, index);
    12. // 获取被删除元素的位置
    13. int numMoved = len - index - 1;
    14. // 如果=0说明删除的是最后一个元素
    15. if (numMoved == 0)
    16. // 复制原数组长度-1
    17. setArray(Arrays.copyOf(elements, len - 1));
    18. else {
    19. // 创建数组,长度为原数组-1
    20. Object[] newElements = new Object[len - 1];
    21. // 该操作与add(int index, E element)方法同理
    22. System.arraycopy(elements, 0, newElements, 0, index);
    23. System.arraycopy(elements, index + 1, newElements, index,
    24. numMoved);
    25. setArray(newElements);
    26. }
    27. return oldValue;
    28. } finally {
    29. // 解锁
    30. lock.unlock();
    31. }
    32. }

    removeRange(int fromIndex, int toIndex)删除指定范围的元素

    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. // 计算新数组长度
    12. int newlen = len - (toIndex - fromIndex);
    13. // 获取结束下标的位置
    14. int numMoved = len - toIndex;
    15. // =0 ,复制原数组从0到newlen位置
    16. if (numMoved == 0)
    17. setArray(Arrays.copyOf(elements, newlen));
    18. else {
    19. // 新建一个数组,长度为newlen的
    20. Object[] newElements = new Object[newlen];
    21. // 该操作与add(int index, E element)方法同理
    22. System.arraycopy(elements, 0, newElements, 0, fromIndex);
    23. System.arraycopy(elements, toIndex, newElements, fromIndex, numMoved);
    24. setArray(newElements);
    25. }
    26. } finally {
    27. lock.unlock();
    28. }
    29. }

    clear()清空集合

    1. // 清空集合中元素
    2. public void clear() {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 数组初始化
    8. setArray(new Object[0]);
    9. } finally {
    10. lock.unlock();
    11. }
    12. }

    addAll(int index, Collection c)在指定位置批量添加元素

    1. // 在指定位置批量添加元素
    2. public boolean addAll(int index, Collection c) {
    3. // 将集合c转换为数组并赋值给cs
    4. Object[] cs = c.toArray();
    5. final ReentrantLock lock = this.lock;
    6. lock.lock();
    7. try {
    8. // 获得原数组
    9. Object[] elements = getArray();
    10. //获取原数组的长度
    11. int len = elements.length;
    12. // 判断下标位置是否合理,不合理抛异常
    13. if (index > len || index < 0)
    14. throw new IndexOutOfBoundsException("Index: "+index+
    15. ", Size: "+len);
    16. // 如果集合c为空,则直接返回false
    17. if (cs.length == 0)
    18. return false;
    19. int numMoved = len - index;
    20. Object[] newElements;
    21. // 如果numMoved等于0,则代表在原集合末尾添加
    22. if (numMoved == 0)
    23. // 复制原集合,个数为原集合元素个数加要添加的元素个数
    24. newElements = Arrays.copyOf(elements, len + cs.length);
    25. else {
    26. // 创建新集合
    27. newElements = new Object[len + cs.length];
    28. // 该操作与add(int index, E element)方法同理
    29. System.arraycopy(elements, 0, newElements, 0, index);
    30. System.arraycopy(elements, index,
    31. newElements, index + cs.length,
    32. numMoved);
    33. }
    34. // 复制数组至新数组
    35. System.arraycopy(cs, 0, newElements, index, cs.length);
    36. setArray(newElements);
    37. return true;
    38. } finally {
    39. lock.unlock();
    40. }
    41. }

  • 相关阅读:
    论文阅读:Towards a Unified View of Parameter-Efficient Transfer Learning对参数高效迁移学习的统一看法
    在Application中使用代码自动判断APP是否为release版本
    【Python第三方包】实现自动化(pyautogui包)
    基于BP神经网络对鸢尾花数据集分类
    汽车托运汽车会产生公里数吗?
    【云原生之Docker】使用docker部署talebook个人图书管理平台
    IDEA打开一个项目时,idea左侧project模式下,不显示项目工程目录的解决方法
    Java 抽象类和接口
    【Linux】进程间通信——共享内存
    MAC 安装 maven
  • 原文地址:https://blog.csdn.net/weixin_49137294/article/details/126922852