• CopyOnWriteArrayList源码分析


    一、CopyOnWriteArrayList

    Copy-On-Write简称COW,是一种用于集合的并发访问的优化策略。

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

    二、CopyOnWriteArrayList 源码分析

    1.CopyOnWriteArrayList构造方法

    1. // 无参构造,初始化一个Object数组
    2. public CopyOnWriteArrayList() {
    3. setArray(new Object[0]);
    4. }
    5. // 有参构造,参数为Collection子类
    6. public CopyOnWriteArrayList(Collection<? extends E> c) {
    7. Object[] elements;
    8. // 初始化类型是CopyOnWriteArrayList
    9. if (c.getClass() == CopyOnWriteArrayList.class)
    10. // 获取参数的Array数组对象,存入
    11. elements = ((CopyOnWriteArrayList<?>)c).getArray();
    12. else {
    13. // 初始化类型不是是CopyOnWriteArrayList,将集合转换为数组
    14. elements = c.toArray();
    15. // c.toArray might (incorrectly) not return Object[] (see 6260652)
    16. // elements不是 Object[],将elements数组复制到一个Object数组中
    17. if (elements.getClass() != Object[].class)
    18. elements = Arrays.copyOf(elements, elements.length, Object[].class);
    19. }
    20. // 存入
    21. setArray(elements);
    22. }

    2、add()方法

    添加新的元素

    1. // 添加新元素
    2. public boolean add(E e) {
    3. // 创建锁的对象
    4. final ReentrantLock lock = this.lock;
    5. // 加锁
    6. lock.lock();
    7. try {
    8. // 获取原数组
    9. Object[] elements = getArray();
    10. // 获取原数组的长度
    11. int len = elements.length;
    12. // 复制出新数组,新数组长度为原数组+1
    13. Object[] newElements = Arrays.copyOf(elements, len + 1);
    14. // 新数组的最后一个下标添加新加入的元素
    15. newElements[len] = e;
    16. // 获取新数组
    17. setArray(newElements);
    18. return true;
    19. } finally {
    20. // 释放锁
    21. lock.unlock();
    22. }
    23. }

    给指定下标添加元素 

    1. // 指定下标添加元素
    2. public void add(int index, E element) {
    3. // 加锁
    4. final ReentrantLock lock = this.lock;
    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. // 创建一个新数组
    15. Object[] newElements;
    16. // 判断是不是添加最后一个元素
    17. int numMoved = len - index;
    18. if (numMoved == 0)
    19. newElements = Arrays.copyOf(elements, len + 1);
    20. else {
    21. // 不是最后一个元素
    22. newElements = new Object[len + 1];
    23. // 复制下标元素之前的元素
    24. System.arraycopy(elements, 0, newElements, 0, index);
    25. // 复制下标元素之后的元素
    26. System.arraycopy(elements, index, newElements, index + 1,
    27. numMoved);
    28. }
    29. // 指定位置添加元素
    30. newElements[index] = element;
    31. // 调用setArray方法将新数组存到array数组
    32. setArray(newElements);
    33. } finally {
    34. // 释放锁
    35. lock.unlock();
    36. }
    37. }

     添加一个集合到指定位置

    1. //添加一个集合到指定位置
    2. public boolean addAll(int index, Collection<? extends E> c) {
    3. // 将集合转换为数组
    4. Object[] cs = c.toArray();
    5. final ReentrantLock lock = this.lock;
    6. lock.lock();
    7. try {
    8. Object[] elements = getArray();
    9. int len = elements.length;
    10. if (index > len || index < 0)
    11. throw new IndexOutOfBoundsException("Index: "+index+
    12. ", Size: "+len);
    13. // 传入集合大小为0
    14. if (cs.length == 0)
    15. return false;
    16. int numMoved = len - index;
    17. Object[] newElements;
    18. // 指定下标在末尾,copy至末尾
    19. if (numMoved == 0)
    20. newElements = Arrays.copyOf(elements, len + cs.length);
    21. else {
    22. // 创建一个长度len + cs.length的数组
    23. newElements = new Object[len + cs.length];
    24. // 复制下标元素之前的元素
    25. System.arraycopy(elements, 0, newElements, 0, index);
    26. // 复制下标元素之后的元素
    27. System.arraycopy(elements, index,
    28. newElements, index + cs.length,
    29. numMoved);
    30. }
    31. // 将集合复制到指定位置
    32. System.arraycopy(cs, 0, newElements, index, cs.length);
    33. // 存入
    34. setArray(newElements);
    35. return true;
    36. } finally {
    37. lock.unlock();
    38. }
    39. }

    3、set()方法

    修改指定范围的值

    1. // 修改
    2. public E set(int index, E element) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. // 获得旧数组
    7. Object[] elements = getArray();
    8. // 获取要修改的值
    9. E oldValue = get(elements, index);
    10. // 旧值与新值不相等
    11. if (oldValue != element) {
    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. // Not quite a no-op; ensures volatile write semantics
    21. setArray(elements);
    22. }
    23. // 返回旧数组
    24. return oldValue;
    25. } finally {
    26. lock.unlock();
    27. }
    28. }

     4、remove()

    删除指定下标元素

    1. // 删除指定下标元素
    2. public E remove(int index) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. // 获取旧数组
    7. Object[] elements = getArray();
    8. int len = elements.length;
    9. // 获取指定位置要删除的值
    10. E oldValue = get(elements, index);
    11. // 删除为数组最后一个元素
    12. int numMoved = len - index - 1;
    13. if (numMoved == 0)
    14. // 复制原数组中,除最后一个元素以外的所有元素,至新数组
    15. setArray(Arrays.copyOf(elements, len - 1));
    16. else {
    17. // 复制原数组中,除删除元素以外的所有元素,至新数组
    18. Object[] newElements = new Object[len - 1];
    19. System.arraycopy(elements, 0, newElements, 0, index);
    20. System.arraycopy(elements, index + 1, newElements, index,
    21. numMoved);
    22. // 存入
    23. setArray(newElements);
    24. }
    25. // 返回旧数组
    26. return oldValue;
    27. } finally {
    28. lock.unlock();
    29. }
    30. }

    删除指定长度的元素 

    1. // 删除指定长度的元素
    2. void removeRange(int fromIndex, int toIndex) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. // 获取旧数组
    7. Object[] elements = getArray();
    8. int len = elements.length;
    9. // 下标越界
    10. if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
    11. throw new IndexOutOfBoundsException();
    12. int newlen = len - (toIndex - fromIndex);
    13. int numMoved = len - toIndex;
    14. // 判断删除位置为后半段
    15. if (numMoved == 0)
    16. // 复制前半段元素
    17. setArray(Arrays.copyOf(elements, newlen));
    18. else {
    19. // 创建一个新数组
    20. Object[] newElements = new Object[newlen];
    21. // 复制删除范围前的元素
    22. System.arraycopy(elements, 0, newElements, 0, fromIndex);
    23. // 复制删除范围后的元素
    24. System.arraycopy(elements, toIndex, newElements,
    25. fromIndex, numMoved);
    26. // 存入
    27. setArray(newElements);
    28. }
    29. } finally {
    30. lock.unlock();
    31. }
    32. }

    三、CopyOnWriteArrayList优点

    实现对CopyOnWrite集合容器写入操作时的线程安全,但同时并不影响进行并发的读取操作。所以CopyOnWrite容器也是一种读写分离的思想。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发集合容器,它们是CopyOnWriteArrayListCopyOnWriteArraySet。 

     

  • 相关阅读:
    Android中layout-sw600dp、layout-w600dp和layout-h600dp的区别
    乐鑫科技收购创新硬件公司 M5Stack 控股权
    mysql基础整理
    简单回顾矩阵的相乘(点乘)230101
    【洛谷】P1114 “非常男女”计划
    建设一个互联网医院系统要花多少钱?
    什么是性能测试混合场景?性能测试混合场景如何测试?
    空调原理与结构、制冷剂类型及相关先进技术
    linux统计日志文件中IP出现的次数,显示次数最多的前十,grep,cat,sort,uniq,head,cut,awk
    npm 常用的命令
  • 原文地址:https://blog.csdn.net/qq_49194330/article/details/126925977