Java中的CopyOnWriteArrayList

时间:2020-02-23 14:36:11  来源:igfitidea点击:

Java中的CopyOnWriteArrayList是List接口的线程安全实现。
在Java 1.5和Collections框架的一部分中添加了CopyOnWriteArrayList。

Java ArrayList和ConcurrentModificationException

ArrayList是List接口的基本实现之一,并且是Java Collections Framework的一部分。
我们可以使用迭代器遍历ArrayList元素。

我们来看一下ArrayList的示例程序。

ConcurrentListExample.java

package com.theitroad.collections;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class ConcurrentListExample {

  public static void main(String[] args) {
      List<String> list = new ArrayList<>();
      list.add("1");
      list.add("2");
      list.add("3");
      list.add("4");
      list.add("5");
      
      //get the iterator
      Iterator<String> it = list.iterator();
      
      //manipulate list while iterating
      while(it.hasNext()){
          System.out.println("list is:"+list);
          String str = it.next();
          System.out.println(str);
          if(str.equals("2"))list.remove("5");
          if(str.equals("3"))list.add("3 found");
          //below code don't throw ConcurrentModificationException
          //because it doesn't change modCount variable of list
          if(str.equals("4")) list.set(1, "4");
      }
  }

}

当我们在上面的程序上运行时,只要修改ArrayList就会得到java.util.ConcurrentModificationException。

发生这种情况是因为ArrayList迭代器在设计上是快速失败的。
这意味着一旦创建了迭代器,如果修改了ArrayList,它将引发ConcurrentModificationException。

如果检查控制台日志,则会注意到Iteratornext()方法引发了异常。
如果您查看ArrayList源代码,则每次我们在引发异常的迭代器上调用next()时,都会调用以下方法。

final void checkForComodification() {
  if (modCount != expectedModCount)
      throw new ConcurrentModificationException();
}

这里的modCount是保存修改次数的ArrayList变量,每当我们使用add,remove或者trimToSize方法时,它都会递增。
" expectedModCount"是迭代器变量,当我们创建与" modCount"具有相同值的迭代器时会初始化该变量。
这就解释了为什么我们使用set方法替换任何现有元素时不会得到异常。

因此,基本上,如果列表大小更改,则迭代器将抛出" ConcurrentModificationException"。

Java中的CopyOnWriteArrayList

有时,如果我们找到一些特定的元素,我们希望从列表中添加或者删除元素,在这种情况下,我们应该使用并发集合类– CopyOnWriteArrayList。
这是java.util.ArrayList的线程安全变体,其中所有可变操作(添加,设置等)都通过对基础数组进行全新复制来实现。

CopyOnWriteArrayList给处理带来了另外的重载,但是当修改量比遍历操作数少时,它非常有效。

如果将实现更改为" CopyOnWriteArrayList",则不会发生任何异常,下面是生成的输出。

list is:[1, 2, 3, 4, 5]
1
list is:[1, 2, 3, 4, 5]
2
list is:[1, 2, 3, 4]
3
list is:[1, 2, 3, 4, 3 found]
4
list is:[1, 4, 3, 4, 3 found]
5

请注意,它允许修改列表,但它不会更改迭代器,并且我们得到的元素与原始列表中的元素相同。