• 基于写时复制技术的并发集合———CopyOnWriteArrayList源码分析


    目录

    概述:

    源码分析:

    构造方法:CopyOnWriteArrayList()

    构造方法:CopyOnWriteArrayList(Collection c)

    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作为一种线程安全的集合,它通过加Lock锁来实现线程安全,并且它的写是复制技术是指在对集合进行添加或修改。删除等操作时,不会直接对当前集合进行操作,而是根据原集合进行复制一份出来,然后再对拷贝的集合进行操作,当对新集合写入完毕时,再将原集合的引用指向当前的新集合,从而实现写时复制。CopyOnWriteArrayList内部使用Object[]类型数组存储数据,它允许多个线程并发读取,但在写入时,只能有一个线程操作当前集合。

    源码分析:

    构造方法:CopyOnWriteArrayList()

    1. // 默认构造方法,创建大小为0的数组
    2. public CopyOnWriteArrayList() {
    3. setArray(new Object[0]);
    4. }

    构造方法:CopyOnWriteArrayList(Collection c)

    1. // 有参构造方法,传入一个集合
    2. public CopyOnWriteArrayList(Collection c) {
    3. Object[] elements;
    4. // 如果传入的集合c的Class对象等于CopyOnWriteArrayList的Class对象
    5. if (c.getClass() == CopyOnWriteArrayList.class)
    6. // 将c集合强转为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. // 如果elements不等于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. // 加锁
    4. final ReentrantLock lock = this.lock;
    5. lock.lock();
    6. try {
    7. // 获取原数组
    8. Object[] elements = getArray();
    9. // 得到下标为index的值
    10. E oldValue = get(elements, index);
    11. // 如果要修改的值和原值不同则执行
    12. if (oldValue != element) {
    13. // 得到新数组的长度
    14. int len = elements.length;
    15. // 根据原数组的内容以及长度进行复制
    16. Object[] newElements = Arrays.copyOf(elements, len);
    17. // 替换下标为index的值
    18. newElements[index] = element;
    19. // 将原数组的引用重新指向新数组
    20. setArray(newElements);
    21. // 如果要修改的值和原值相同则执行
    22. } else {
    23. // Not quite a no-op; ensures volatile write semantics
    24. // 将原数组的引用重新指向原数组
    25. setArray(elements);
    26. }
    27. // 将原数组中index下标的值返回
    28. return oldValue;
    29. } finally {
    30. // 解锁
    31. lock.unlock();
    32. }
    33. }

     add(E e):在尾部添加新元素

    1. // 添加新元素
    2. public boolean add(E e) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. Object[] elements = getArray();
    7. int len = elements.length;
    8. // 复制原数组,并且数组长度较原数组 + 1
    9. Object[] newElements = Arrays.copyOf(elements, len + 1);
    10. // 将添加的元素e添加到新数组的末尾
    11. newElements[len] = e;
    12. // 重新指向
    13. setArray(newElements);
    14. return true;
    15. } finally {
    16. lock.unlock();
    17. }
    18. }

    add(int index, E element):在指定位置添加值

    1. // 在指定位置添加值
    2. public void add(int index, E element) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. Object[] elements = getArray();
    7. int len = elements.length;
    8. // 如果下标大于数组长度或小于0 抛下标越界异常
    9. if (index > len || index < 0)
    10. throw new IndexOutOfBoundsException("Index: "+index+
    11. ", Size: "+len);
    12. Object[] newElements;
    13. int numMoved = len - index;
    14. // 如果添加位置是最后一个位置,
    15. if (numMoved == 0)
    16. // 新数组长度直接加1就行
    17. newElements = Arrays.copyOf(elements, len + 1);
    18. else {
    19. // 否则,新创建一个数组长度为len + 1 的数组
    20. newElements = new Object[len + 1];
    21. // 分别复制要插入位置前后两边的数组
    22. System.arraycopy(elements, 0, newElements, 0, index);
    23. System.arraycopy(elements, index, newElements, index + 1,
    24. numMoved);
    25. }
    26. // 将新元素加入到空出来的那个下标位置
    27. newElements[index] = element;
    28. setArray(newElements);
    29. } finally {
    30. lock.unlock();
    31. }
    32. }

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

    1. // 删除指定下标的元素
    2. public E remove(int index) {
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. Object[] elements = getArray();
    7. int len = elements.length;
    8. // 获取原数组中下标为index的值oldValue
    9. E oldValue = get(elements, index);
    10. // 计算要删除的元素是否是最后一个
    11. int numMoved = len - index - 1;
    12. // 如果是
    13. if (numMoved == 0)
    14. // 直接复原原数组直到最后一个元素之前并重新指向
    15. setArray(Arrays.copyOf(elements, len - 1));
    16. else {
    17. // 创建原数组长度 - 1的数组
    18. Object[] newElements = new Object[len - 1];
    19. // 同上
    20. System.arraycopy(elements, 0, newElements, 0, index);
    21. System.arraycopy(elements, index + 1, newElements, index,
    22. numMoved);
    23. setArray(newElements);
    24. }
    25. return oldValue;
    26. } finally {
    27. lock.unlock();
    28. }
    29. }

    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. // 如果开始下标<0 或者结束下标大于数组长度或者结束下标小于开始下标
    9. // 抛下标越界异常
    10. if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
    11. throw new IndexOutOfBoundsException();
    12. // 计算新数组长度
    13. int newlen = len - (toIndex - fromIndex);
    14. // 判断结束下标是否是最后一个下标
    15. int numMoved = len - toIndex;
    16. // 如果是
    17. if (numMoved == 0)
    18. // 则直接复制原数组从头复制到newlen位置
    19. setArray(Arrays.copyOf(elements, newlen));
    20. else {
    21. // 新建一个数组,长度为newlen的
    22. Object[] newElements = new Object[newlen];
    23. // 同上
    24. System.arraycopy(elements, 0, newElements, 0, fromIndex);
    25. System.arraycopy(elements, toIndex, newElements,
    26. fromIndex, numMoved);
    27. setArray(newElements);
    28. }
    29. } finally {
    30. lock.unlock();
    31. }
    32. }

     clear():清空集合

    1. // 清空集合中元素
    2. public void clear() {
    3. final ReentrantLock lock = this.lock;
    4. // 加锁
    5. lock.lock();
    6. try {
    7. // 将原数组修改我object[]类型的空数组
    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. // 同上
    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. // 修改引用
    37. setArray(newElements);
    38. return true;
    39. } finally {
    40. lock.unlock();
    41. }
    42. }
    43. }

  • 相关阅读:
    【研究生学术英语读写教程翻译 中国科学院大学Unit3】
    CSS :has伪类
    实战 | 如何用 Python 统计 Jira 数据并可视化
    微信小程序——标签wxml、样式wxss、js、json
    NNDL 实验六 卷积神经网络
    Windows下安装MySQL8详细教程
    C语言中的大端字节序和小端字节序是什么?如何进行字节序的转换?
    羧酸/氨基/羟基功能化修饰荧光聚苯乙烯AIE微球/AIE三苯胺衍生物荧光微球的研究与制备
    再谈ThreadPoolExecutor
    Linux--基础IO
  • 原文地址:https://blog.csdn.net/qq_17845335/article/details/126921426