Java CopyOnWriteArrayList与示例
Java中的CopyOnWriteArrayList与其他众所周知的对应ArrayList一样,实现了List接口,并且是java.util.concurrent包的一部分。 CopyOnWriteArrayList与ArrayList的不同之处在于它是ArrayList的线程安全变体。
有关Java中的CopyOnWriteArrayList和ArrayList之间的更多区别,请参阅此文章Java中的ArrayList和CopyOnWriteArrayList之间的区别
Java CopyOnWriteArrayList如何线程安全
Java中的CopyOnWriteArrayList与ArrayList一样,使用Object类型的数组存储其元素。为了线程安全,如其名称所示,CopyOnWriteArrayList的实现为任何修改操作(如添加,设置,替换等)创建基础数组的新副本。
当遍历操作比突变更多时,这使得CopyOnWriteArrayList是一个不错的选择,因为可以在不进行任何推断的情况下对List进行迭代和并发修改,因为迭代将在List的单独副本上进行。
Java CopyOnWriteArrayList构造函数
CopyOnWriteArrayList()–创建一个空列表。
CopyOnWriteArrayList(Collection <?extends E> c)–创建一个包含指定集合元素的列表,其顺序由集合的迭代器返回。
CopyOnWriteArrayList(E [] toCopyIn)–创建一个包含给定数组副本的列表。
Java示例,创建一个CopyOnWriteArrayList
这是一个简单的示例,显示了如何创建CopyOnWriteArrayList并向其中添加元素。
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyList {
public static void main(String[] args) {
//creating CopyOnWriteArrayList
List<String> carList = new CopyOnWriteArrayList<String>();
carList.add("Audi");
carList.add("Jaguar");
carList.add("Mini Cooper");
carList.add("BMW");
System.out.println("List elements- " + carList);
}
}
输出:
List elements- [Audi, Jaguar, Mini Cooper, BMW]
CopyOnWriteArrayList返回故障保护迭代器
Java中的CopyOnWriteArrayList返回的迭代器是故障安全的,这意味着即使在创建迭代器后随时对列表进行结构修改,也可以保证迭代器不会引发ConcurrentModificationException。
为CopyOnWriteArrayList创建一个迭代器时,它将获得要迭代的基础数组的不可变副本。该数组在迭代器的生命周期内永不改变,因此是不可能的。
但请注意,由于迭代是在单独的副本上完成的,因此在迭代过程中不会反映出CopyOnWriteArrayList中的任何修改。
CopyOnWriteArrayList迭代示例
我们来看一个CopyOnWriteArrayList中的迭代示例。为了更清楚起见,我们先迭代一个ArrayList,同时另一个线程同时修改它,以查看ArrayList发生了什么,然后我们使用CopyOnWriteArrayList看到相同的示例。
public class CopyList {
public static void main(String[] args) {
//creating CopyOnWriteArrayList
List<String> carList = new ArrayList<String>();
carList.add("Audi");
carList.add("Jaguar");
carList.add("Mini Cooper");
carList.add("BMW");
Thread t1 = new Thread(new ItrClass(carList));
Thread t2 = new Thread(new ModClass(carList));
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("List elements in Main- " + carList);
}
}
// Thread class for iteration
class ItrClass implements Runnable{
List<String> carList;
public ItrClass(List<String> carList){
this.carList = carList;
}
@Override
public void run() {
Iterator<String> i = carList.iterator();
while (i.hasNext()){
System.out.println(i.next());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//Thread class for modifying list
class ModClass implements Runnable{
List<String> carList;
public ModClass(List<String> carList){
this.carList = carList;
}
@Override
public void run() {
System.out.println("Adding new value to the list");
carList.add("Mercedes");
}
}
输出:
Adding new value to the list Audi Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891) at com.theitroad.ItrClass.run(CopyList.java:41) at java.base/java.lang.Thread.run(Thread.java:844) List elements in Main- [Audi, Jaguar, Mini Cooper, BMW, Mercedes]
从ArrayList可以看到,如果列表在迭代时被修改,则抛出ConcurrentModificationException。
使用CopyOnWriteArrayList
现在,在同一示例中,我们可以将ArrayList更改为CopyOnWriteArrayList。
List<String> carList = new CopyOnWriteArrayList<String>();
与该输出是
Adding new value to the list Audi Jaguar Mini Cooper BMW List elements in Main- [Audi, Jaguar, Mini Cooper, BMW, Mercedes]
如我们现在所见,不会引发ConcurrentModificationException,但是迭代器不会在迭代新的元素时显示新添加的元素。
CopyOnWriteArrayList中不允许使用迭代器的添加,删除方法
制作新副本使我们可以方便地迭代List,而不必担心ConcurrentModificationException,但与此同时,迭代器的元素更改操作(如remove,set和add在CopyOnWriteArrayList中是不支持的)。这些方法抛出UnsupportedOperationException。
public class CopyList {
public static void main(String[] args) {
//creating CopyOnWriteArrayList
List<String> carList = new CopyOnWriteArrayList<String>();
carList.add("Audi");
carList.add("Jaguar");
carList.add("Mini Cooper");
carList.add("BMW");
Iterator<String> itr = carList.iterator();
while (itr.hasNext()){
String str = itr.next();
if(str.equals("Jaguar")) {
// removing using iterator's remove method
itr.remove();
}
}
}
}
输出:
Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1117) at com.theitroad.CopyList.main(CopyList.java:21)
如我们所见,在这里使用迭代器的remove方法会导致抛出UnsupportedOperationException。
在Java中使用CopyOnWriteArrayList的优缺点
当遍历操作的次数大于突变次数时,CopyOnWriteArrayList的性能会很好,因为我们无需显式同步CopyOnWriteArrayList即可在多线程环境中对其进行迭代。
通常,使用CopyOnWriteArrayList会很昂贵,因为增加了在发生突变操作时创建副本并不断更改基础数组的任务。
即使在迭代过程中对列表进行了并发修改,也可以保证CopyOnWriteArrayList不会引发ConcurrentModificationException。同时,迭代器的元素更改操作不受支持。

