Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理
copyonwritearrayset介绍
它是线程安全的无序的集合,可以将它理解成线程安全的hashset。有意思的是,copyonwritearrayset和hashset虽然都继承于共同的父类abstractset;但是,hashset是通过“散列表(hashmap)”实现的,而copyonwritearrayset则是通过“动态数组(copyonwritearraylist)”实现的,并不是散列表。
和copyonwritearraylist类似,copyonwritearrayset具有以下特性:
1. 它最适合于具有以下特征的应用程序:set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
2. 它是线程安全的。
3. 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
4. 迭代器支持hasnext(), next()等不可变操作,但不支持可变 remove()等 操作。
5. 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
copyonwritearrayset原理和数据结构
copyonwritearrayset的数据结构,如下图所示:
说明:
1. copyonwritearrayset继承于abstractset,这就意味着它是一个集合。
2. copyonwritearrayset包含copyonwritearraylist对象,它是通过copyonwritearraylist实现的。而copyonwritearraylist本质是个动态数组队列,
所以copyonwritearrayset相当于通过通过动态数组实现的“集合”! copyonwritearraylist中允许有重复的元素;但是,copyonwritearrayset是一个集合,所以它不能有重复集合。因此,copyonwritearraylist额外提供了addifabsent()和addallabsent()这两个添加元素的api,通过这些api来添加元素时,只有当元素不存在时才执行添加操作!
至于copyonwritearrayset的“线程安全”机制,和copyonwritearraylist一样,是通过volatile和互斥锁来实现的。这个在前一章节介绍copyonwritearraylist时数据结构时,已经进行了说明,这里就不再重复叙述了。
copyonwritearrayset函数列表
// 创建一个空 set。 copyonwritearrayset() // 创建一个包含指定 collection 所有元素的 set。 copyonwritearrayset(collection<? extends e> c) // 如果指定元素并不存在于此 set 中,则添加它。 boolean add(e e) // 如果此 set 中没有指定 collection 中的所有元素,则将它们都添加到此 set 中。 boolean addall(collection<? extends e> c) // 移除此 set 中的所有元素。 void clear() // 如果此 set 包含指定元素,则返回 true。 boolean contains(object o) // 如果此 set 包含指定 collection 的所有元素,则返回 true。 boolean containsall(collection<?> c) // 比较指定对象与此 set 的相等性。 boolean equals(object o) // 如果此 set 不包含任何元素,则返回 true。 boolean isempty() // 返回按照元素添加顺序在此 set 中包含的元素上进行迭代的迭代器。 iterator<e> iterator() // 如果指定元素存在于此 set 中,则将其移除。 boolean remove(object o) // 移除此 set 中包含在指定 collection 中的所有元素。 boolean removeall(collection<?> c) // 仅保留此 set 中那些包含在指定 collection 中的元素。 boolean retainall(collection<?> c) // 返回此 set 中的元素数目。 int size() // 返回一个包含此 set 所有元素的数组。 object[] toarray() // 返回一个包含此 set 所有元素的数组;返回数组的运行时类型是指定数组的类型。 <t> t[] toarray(t[] a)
copyonwritearrayset是通过copyonwritearraylist实现的,它的api基本上都是通过调用copyonwritearraylist的api来实现的。相信对copyonwritearraylist了解的话,对copyonwritearrayset的了解是水到渠成的事;所以,这里就不再对copyonwritearrayset的代码进行详细的解析了。
copyonwritearrayset示例
下面,我们通过一个例子去对比hashset和copyonwritearrayset。
import java.util.*; import java.util.concurrent.*; /* * copyonwritearrayset是“线程安全”的集合,而hashset是非线程安全的。 * * 下面是“多个线程同时操作并且遍历集合set”的示例 * (01) 当set是copyonwritearrayset对象时,程序能正常运行。 * (02) 当set是hashset对象时,程序会产生concurrentmodificationexception异常。 * * */ public class copyonwritearraysettest1 { // todo: set是hashset对象时,程序会出错。 //private static set<string> set = new hashset<string>(); private static set<string> set = new copyonwritearrayset<string>(); public static void main(string[] args) { // 同时启动两个线程对set进行操作! new mythread("ta").start(); new mythread("tb").start(); } private static void printall() { string value = null; iterator iter = set.iterator(); while(iter.hasnext()) { value = (string)iter.next(); system.out.print(value+", "); } system.out.println(); } private static class mythread extends thread { mythread(string name) { super(name); } @override public void run() { int i = 0; while (i++ < 10) { // “线程名” + "-" + "序号" string val = thread.currentthread().getname() + "-" + (i%6); set.add(val); // 通过“iterator”遍历set。 printall(); } } } }
(某一次)运行结果:
ta-1, tb-1, ta-1, tb-1, ta-1, tb-1, ta-1, ta-2, tb-1, ta-1, ta-2, tb-1, tb-2, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-1, ta-3, tb-1, tb-3, ta-2, ta-4, tb-2, ta-1, ta-3, tb-1, tb-3, ta-2, ta-4, tb-2, tb-4, ta-3, ta-1, tb-3, tb-1, ta-4, ta-2, tb-4, tb-2, ta-5, ta-3, ta-1, tb-3, tb-1, ta-4, ta-2, tb-4, tb-2, ta-5, ta-3, tb-5, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, tb-0, ta-4, ta-1, tb-4, tb-1, ta-5, ta-2, tb-5, tb-2, ta-0, ta-3, tb-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-5, ta-0, tb-0, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-4, ta-3, tb-4, tb-3, ta-5, ta-4, tb-5, tb-4, ta-0, ta-5, tb-0, tb-5, ta-1, ta-0, tb-1, tb-0, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-4, ta-3, tb-4, tb-3, ta-5, tb-5, ta-0, tb-0, ta-4, ta-1, tb-4, tb-1, ta-5, ta-2, tb-5, tb-2, ta-0, ta-3, tb-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, tb-0, ta-4, tb-4, ta-5, tb-5, ta-0, tb-0,
结果说明:
由于set是集合对象,因此它不会包含重复的元素。
如果将源码中的set改成hashset对象时,程序会产生concurrentmodificationexception异常。
推荐阅读
-
Java concurrency线程池之线程池原理(一)_动力节点Java学院整理
-
Java concurrency线程池之线程池原理(二)_动力节点Java学院整理
-
Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理
-
Java concurrency集合之LinkedBlockingDeque_动力节点Java学院整理
-
Java concurrency之Condition条件_动力节点Java学院整理
-
Java concurrency之CountDownLatch原理和示例_动力节点Java学院整理
-
Filter、Servlet、Listener的学习_动力节点Java学院整理
-
ehcache开源缓存框架_动力节点Java学院整理
-
servlet之cookie简介_动力节点Java学院整理
-
tomcat目录结构简介_动力节点Java学院整理