Java CopyOnWriteArraySet与示例
Java中的CopyOnWriteArraySet扩展了AbstractSet,后者又实现了Set接口,并且是java.util.concurrent包的一部分。 CopyOnWriteArraySet与Java Collections框架中其他Set实现的不同之处在于它是线程安全的。
Java中的CopyOnWriteArraySet内部实现
CopyOnWriteArraySet内部使用CopyOnWriteArrayList进行所有操作,并共享相同的基本属性。在Java中的CopyOnWriteArraySet类中,CopyOnWriteArrayList定义如下:
private final CopyOnWriteArrayList<E> al;
创建CopyOnwriteArraySet时,将初始化此CopyOnWriteArrayList字段并将其用于存储元素。
例如,当使用不带参数的构造函数创建CopyOnwriteArraySet时。
public CopyOnWriteArraySet() { al = new CopyOnWriteArrayList<E>(); }
CopyOnWriteArraySet的功能
本文中讨论的Java中的CopyOnWriteArraySet的一些功能是
CopyOnWriteArraySet是Set实现,因此不允许重复元素。
CopyOnWriteArraySet是线程安全的。
由于CopyOnWriteArraySet在内部使用CopyOnWriteArrayList,所以就像在CopyOnWriteArrayList中一样,所有可变操作(添加,设置等)都将为基础数组创建一个单独的副本,这样就不会造成线程干扰。
Java中CopyOnWriteArraySet返回的迭代器是故障安全的,这意味着即使在创建迭代器后随时对Set进行结构修改,也可以保证迭代器不会引发ConcurrentModificationException。
迭代器的元素更改操作(例如添加,删除)是不支持的,并引发UnsupportedOperationException。
Java CopyOnWriteArraySet构造函数
CopyOnWriteArraySet()–创建一个空集。
CopyOnWriteArraySet(Collection <?extends E> c)–创建一个包含指定集合的所有元素的集合。
创建CopyOnWriteArraySet的Java示例
这是一个简单的示例,显示了如何创建CopyOnWriteArraySet并向其中添加元素。
public class ConcurrentSet { public static void main(String[] args) { Set<String> carSet = new CopyOnWriteArraySet<String>(); carSet.add("Audi"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add("Mini Cooper"); carSet.add("BMW"); carSet.add(null); for(String car : carSet) { System.out.println("Car- " + car); } } }
输出:
Car- Audi Car- Jaguar Car- BMW Car- Mini Cooper Car- null
从输出中可以看到,即使将" BMW"添加了两次,也不允许在CopyOnWriteArraySet中进行重复操作,但只能存储一次。同样允许一个空值。
CopyOnWriteArraySet返回故障保护迭代器
Java中CopyOnWriteArraySet返回的迭代器是故障安全的,这意味着即使在创建迭代器后随时对Set进行结构修改,也可以保证迭代器不会引发ConcurrentModificationException。
为CopyOnWriteArraySet创建迭代器时,它将获得要迭代的基础数组的不可变副本。该数组在迭代器的生命周期内永不改变,因此是不可能的。
但请注意,由于迭代是在单独的副本上完成的,因此在迭代过程中不会反映出CopyOnWriteArraySet中的任何修改。
CopyOnWriteArraySet迭代示例
我们来看一个CopyOnWriteArraySet中的迭代示例。为了更清楚地说明,我们先对HashSet进行迭代,同时还要由另一个线程同时对其进行修改,以查看具有快速失败迭代器的HashSet发生了什么,然后我们将使用CopyOnWriteArraySet查看相同的示例。
public class SetItr { public static void main(String[] args) { Set<String> carSet = new HashSet<String>(); carSet.add("Audi"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add("Mini Cooper"); Thread t1 = new Thread(new ItrSet(carSet)); Thread t2 = new Thread(new ModSet(carSet)); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //Thread class for iteration class ItrSet implements Runnable{ Set<String> carSet; public ItrSet(Set<String> carSet){ this.carSet = carSet; } @Override public void run() { Iterator<String> i = carSet.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 Set class ModSet implements Runnable{ Set<String> carSet; public ModSet(Set<String> carSet){ this.carSet = carSet; } @Override public void run() { System.out.println("Adding new value to the Set"); carSet.add("Mercedes"); } }
输出:
Adding new value to the Set Audi Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1498) at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1521) at com.theitroad.ItrSet.run(SetItr.java:40) at java.base/java.lang.Thread.run(Thread.java:844)
如我们所见,在迭代HashSet时检测到结构修改,将引发ConcurrentModificationException。
使用CopyOnWriteArraySet
通过在相同的代码中将HashSet更改为CopyOnWriteArraySet并运行它。
Set<String> carSet = new CopyOnWriteArraySet<String>();
我们可以将输出作为
Adding new value to the Set Audi Jaguar BMW Mini Cooper
现在,不会引发ConcurrentModificationException,但由于迭代是在单独的副本上完成的,因此不会在迭代中显示添加的新值。
CopyOnWriteArraySet中不允许使用迭代器的添加,删除方法
由于CopyOnWriteArraySet中元素的迭代是在单独的副本迭代器的元素更改操作(如remove is不支持)上完成的。这些方法抛出UnsupportedOperationException。
public class SetItr { public static void main(String[] args) { Set<String> carSet = new CopyOnWriteArraySet<String>(); carSet.add("Audi"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add("Mini Cooper"); Iterator<String> itr = carSet.iterator(); while (itr.hasNext()){ String str = itr.next(); if(str.equals("BMW")) { // 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.SetItr.main(SetItr.java:21)
有关CopyOnWriteArraySet的要点
CopyOnWriteArraySet最适合于集大小较小,只读操作多于可变操作的应用,并且我们需要防止遍历期间线程之间的干扰。
交互操作(添加,设置,删除等)的成本很高,这是因为添加了创建基础数组副本的任务。
即使在迭代过程中对Set进行了并发修改,也保证CopyOnWriteArraySet不会引发ConcurrentModificationException。同时,迭代器的元素更改操作(例如remove)是不支持的。