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。同时,迭代器的元素更改操作不受支持。