当前位置:   article > 正文

基于写时复制技术的并发集合———CopyOnWriteArrayList源码分析_copyonwritearraylist 并发新增和删除

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<? extends E> c)

  1. // 有参构造方法,传入一个集合
  2. public CopyOnWriteArrayList(Collection<? extends E> 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<? extends E> c):在指定下标位置批量添加元素

  1. // 在指定下标位置批量添加元素
  2. public boolean addAll(int index, Collection<? extends E> 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. }

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/821952
推荐阅读
相关标签
  

闽ICP备14008679号