CopyOnWriteArraySet简介
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CopyOnWriteArraySet 是通过 CopyOnWriteArrayList 实现的,它的 API 基本上都是通过调用 CopyOnWriteArrayList 的 API 来实现的。相信对 CopyOnWriteArrayList 了解的话,对 CopyOnWriteArraySet 的了解是水到渠成的事;所以,这里
至于 CopyOnWriteArraySet 的“线程安全”机制,和 CopyOnWriteArrayList 一样,是通过 volatile 和互斥锁来实现的。 这个在前一章节介绍 CopyOnWriteArrayList 时数据结构时,已经进行了说明,这里就不再重复叙述了。
1/5
CopyOnWriteArraySet 函 数 列 表
/* * CopyOnWriteArraySet 是“线程安全”的集合,而 HashSet 是非线程安全的。 * * 下面是“多个线程同时操作并且遍历集合 set”的示例 * (01) 当 set 是 CopyOnWriteArraySet 对象时,程序能正常运行。 * (02) 当 set 是 HashSet 对象时,程序会产生 ConcurrentModificationException 异常。 * * */ public class CopyOnWriteArraySetTest1 {
2/5
就不再对 CopyOnWriteArraySet 的代码进行详细的解析了。
CopyOnWriteArraySet 示 例 下面,我们通过一个例子去对比 HashSet 和 CopyOnWriteArraySet。
import java.util.*; import java.util.concurrent.*;
// 同时启动两个线程对 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();
Java concurrency 集合之 CopyOnWriteArraySet
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 来添加元素时,只有当元素不存在时才执行添加操作!
(某一次)运行结果:
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,
// 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) {
4/5
结果说明: 由于 set 是集合对象,因此它不会包含重复的元素。 如果将源码中的 set 改成 HashSet 对象时,程序会产生 ConcurrentModificationException 异常。 本文作者:skywang12345
5/5
}
private static class MyThread extends Thread {
3/5
MyThread(String name) { super(name);
} @Override public void run() {
int i = 0; while (i++ < 10) {
// “线程名” + "-" + "序号" Leabharlann Baidutring val = Thread.currentThread().getName() + "-" + (i%6); set.add(val); // 通过“Iterator”遍历 set。 printAll(); } } } }
// 创建一个空 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 一样,是通过 volatile 和互斥锁来实现的。 这个在前一章节介绍 CopyOnWriteArrayList 时数据结构时,已经进行了说明,这里就不再重复叙述了。
1/5
CopyOnWriteArraySet 函 数 列 表
/* * CopyOnWriteArraySet 是“线程安全”的集合,而 HashSet 是非线程安全的。 * * 下面是“多个线程同时操作并且遍历集合 set”的示例 * (01) 当 set 是 CopyOnWriteArraySet 对象时,程序能正常运行。 * (02) 当 set 是 HashSet 对象时,程序会产生 ConcurrentModificationException 异常。 * * */ public class CopyOnWriteArraySetTest1 {
2/5
就不再对 CopyOnWriteArraySet 的代码进行详细的解析了。
CopyOnWriteArraySet 示 例 下面,我们通过一个例子去对比 HashSet 和 CopyOnWriteArraySet。
import java.util.*; import java.util.concurrent.*;
// 同时启动两个线程对 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();
Java concurrency 集合之 CopyOnWriteArraySet
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 来添加元素时,只有当元素不存在时才执行添加操作!
(某一次)运行结果:
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,
// 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) {
4/5
结果说明: 由于 set 是集合对象,因此它不会包含重复的元素。 如果将源码中的 set 改成 HashSet 对象时,程序会产生 ConcurrentModificationException 异常。 本文作者:skywang12345
5/5
}
private static class MyThread extends Thread {
3/5
MyThread(String name) { super(name);
} @Override public void run() {
int i = 0; while (i++ < 10) {
// “线程名” + "-" + "序号" Leabharlann Baidutring val = Thread.currentThread().getName() + "-" + (i%6); set.add(val); // 通过“Iterator”遍历 set。 printAll(); } } } }
// 创建一个空 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)