目录

一、简介

二、常用API

三、使用示例

四、源码阅读

五、总结


一、简介

CopyOnWriteArraySet是线程安全的无序集合,可以将它理解成线程安全的HashSet。CopyOnWriteArraySet和HashSet都继承于共同的父类AbstractSet;但是,HashSet是通过“散列表(HashMap)”实现的,而CopyOnWriteArraySet则是通过上一节介绍的CopyOnWriteArrayList写时复制ArrayList实现的,并不是散列表。CopyOnWriteArraySet和CopyOnWriteArrayList区别就是CopyOnWriteArraySet中不允许有重复元素存在

CopyOnWriteArraySet,也是基于“写时复制”的思想。事实上,CopyOnWriteArraySet内部引用了一个CopyOnWriteArrayList对象,以“组合”方式,委托CopyOnWriteArrayList对象实现了所有API功能。声明如下:

public class CopyOnWriteArraySet<E>
extends AbstractSet<E>
implements Serializable

可以看到,CopyOnWriteArraySet继承自AbstractSet,并且实现了序列化接口。

二、常用API

【a】构造方法

CopyOnWriteArraySet()

创建一个空集

CopyOnWriteArraySet(Collection<? extends E> c)

创建一个包含指定集合的所有元素的集合。

【b】常用方法

方法返回值类型

方法描述

boolean

add(E e)

如果指定的元素尚未出现,则将其添加到此集合

boolean

addAll(Collection<? extends E> c)

将指定集合中的所有元素添加到此集合(如果它们尚未出现)

void

clear()

从这个集合中删除所有的元素

boolean

contains(Object o)

如果该集合包含指定的元素,则返回true

boolean

containsAll(Collection<?> c)

如果此集合包含指定集合的所有元素,则返回true

boolean

equals(Object o)

将指定的对象与此集合进行相等性比较

void

forEach(Consumer<? super E> action)

为可迭代的每个元素执行给定的操作,直到处理完所有元素或操作引发异常

boolean

isEmpty()

如果该集合不包含任何元素,则返回true

Iterator<E>

iterator()

按照添加元素的顺序,在这个集合中包含的元素上返回一个迭代器

boolean

remove(Object o)

如果指定的元素存在,则从该集合中移除它

boolean

removeAll(Collection<?> c)

从该集合中移除指定集合中包含的所有元素

boolean

retainAll(Collection<?> c)

仅保留此集合中包含在指定集合中的元素

int

size()

返回该集合中元素的数目。

Spliterator<E>

spliterator()

按照添加元素的顺序,在这个集合的元素上返回一个Spliterator

Object[]

toArray()

返回一个包含该集合中所有元素的数组

<T> T[]

toArray(T[] a)

返回一个包含该集合中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型

三、使用示例

下面通过一个简单的示例说明如何使用CopyOnWriteArraySet:

public class T06_CopyOnWriteArraySet {private static Set<String> set = new CopyOnWriteArraySet<>();static {for (int i = 0; i < 10; i++) {set.add(String.valueOf(i));}}public static void main(String[] args) {new Thread(() -> {for (int i = 100; i < 110; i++) {set.add(String.valueOf(i));try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}iterator = set.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}}
}

运行结果:

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 100 101 

一个线程往集合中写数据,一个线程从集合中读取数据,可见,并发操作CopyOnWriteArraySet并不会发生ConcurrentModificationException并发修改异常。

四、源码阅读

CopyOnWriteArraySet是通过CopyOnWriteArrayList实现的,底层很多方法基本上全是调用的CopyOnWriteArrayList中的方法来实现,具体可以参考上一篇关于CopyOnWriteArrayList源码的详细介绍。

重要属性说明:

//序列化ID
private static final long serialVersionUID = 5457747651344034263L;//通过聚合CopyOnWriteArrayList动态数组来完成所有功能
private final CopyOnWriteArrayList<E> al;

构造方法:

/*** 创建一个空集合.*/
public CopyOnWriteArraySet() {//实际上是创建了CopyOnWriteArrayList,即一个长度为0的对象数组al = new CopyOnWriteArrayList<E>();
}/*** 创建一个包含指定集合的所有元素的集合.*/
public CopyOnWriteArraySet(Collection<? extends E> c) {if (c.getClass() == CopyOnWriteArraySet.class) {@SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =(CopyOnWriteArraySet<E>)c;//创建包含指定集合的集合al = new CopyOnWriteArrayList<E>(cc.al);}else {al = new CopyOnWriteArrayList<E>();//只有元素不存在,才添加进去al.addAllAbsent(c);}
}

常用方法说明:

【a】add(E e)、addAll(Collection<? extends E> c): 底层调用CopyOnWriteArrayList.addIfAbsent,只有元素不存在的时候,才会添加成功,否则添加失败,返回false.

/*** 如果指定的元素尚未出现,则将其添加到此集合。更正式地说,如果集合不包含元素e,则将指定的元素e添加到该集合中。如果该集合已经包含元素e,则调用将保持集合不变,并返回false.*/
public boolean add(E e) {return al.addIfAbsent(e);
}//同理
public boolean addAll(Collection<? extends E> c) {return al.addAllAbsent(c) > 0;
}

【b】remove(Object o) :删除集合中指定的元素

/*** 如果指定的元素存在,则从该集合中移除它*/
public boolean remove(Object o) {//在CopyOnWriteArrayList,根据元素找出在数组中的下标,然后再进行删除return al.remove(o);
}

【c】size()、isEmpty()

/*** 返回该集合中元素的数目*/
public int size() {return al.size();
}/*** 如果这个集合不包含元素,则返回true*/
public boolean isEmpty() {return al.isEmpty();
}

【d】迭代

/*** 返回的迭代器提供了构建迭代器时set状态的快照。遍历迭代器时不需要同步.*/
public Iterator<E> iterator() {return al.iterator();
}

其他更加详细的方法可以参考上一篇文章CopyOnWriteArrayList。

五、总结

CopyOnWriteArraySet底层都是通过CopyOnWriteArrayList来实现的,同样也是基于“写时复制”的思想,那么它的特性也和CopyOnWriteArrayList是类似的,主要有以下几点:

  1. 适合“读多写少”且数据量不大的场景;
  2. 线程安全;
  3. 内存的使用较多;
  4. 迭代是对快照进行的,不会抛出ConcurrentModificationException,且迭代过程中不支持修改操作;

并发编程学习之CopyOnWriteArraySet相关推荐

  1. java中解决脏读_java并发编程学习之脏读代码示例及处理

    使用interrupt()中断线程     当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即 ...

  2. libevent c++高并发网络编程_高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  3. java公平锁和非公平锁_java并发编程学习之再谈公平锁和非公平锁

    在java并发编程学习之显示锁Lock里有提过公平锁和非公平锁,我们知道他的使用方式,以及非公平锁的性能较高,在AQS源码分析的基础上,我们看看NonfairSync和FairSync的区别在什么地方 ...

  4. 高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  5. Java高并发编程学习(三)java.util.concurrent包

    简介 我们已经学习了形成Java并发程序设计基础的底层构建块,但对于实际编程来说,应该尽可能远离底层结构.使用由并发处理的专业人士实现的较高层次的结构要方便得多.要安全得多.例如,对于许多线程问题,可 ...

  6. java并行任务,Java 并发编程学习(五):批量并行执行任务的两种方式

    Java 并发编程学习(五):批量并行执行任务的两种方式 背景介绍 有时候我们需要执行一批相似的任务,并且要求这些任务能够并行执行.通常,我们的需求会分为两种情况: 并行执行一批任务,等待耗时最长的任 ...

  7. java并发编程学习一

    java并发编程学习一 什么是进程和线程? 进程是操作系统进行资源分配的最小单位 进程跟进程之间的资源是隔离的,同一个进程之中的线程可以共享进程的资源. 线程是进程的一个实体,是CPU 调度和分派的基 ...

  8. 基于《狂神说Java》JUC并发编程--学习笔记

    前言: 本笔记仅做学习与复习使用,不存在刻意抄袭. -------------------------------------------------------------------------- ...

  9. 【并发入门】Java 并发编程学习笔记

    注:该笔记主要记录自 B站 up主 遇见狂神说的个人空间_哔哩哔哩_bilibili 1.什么是 JUC Java 工具类中的 并发编程包 学习:源码 + 官方文档 业务:普通的线程代码 Thread ...

  10. Java并发编程学习 + 原理分析(建议收藏)

    总结不易,如果对你有帮助,请点赞关注支持一下 微信搜索程序dunk,关注公众号,获取博客源码 Doug Lea是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为 ...

最新文章

  1. Exchange数据库无法装载的问题
  2. Oracle中的数据类型和数据类型之间的转换
  3. BA-Alerton系统简介
  4. 全球及中国冶金行业战略决策与盈利前景分析报告2022版
  5. Ubuntu安装VMware Tools的方法
  6. Maven for Eclipse 第二章 ——安装 m2eclipse插件
  7. .Net基础体系和跨框架开发普及
  8. Enterprise Library 4.1 快速上手(图)
  9. java gson_Java 中 Gson的使用
  10. chartjs更新数据 vue_vue.js - 在vue中 怎么更改chart图表的文字大小
  11. eclipse java不能编译_eclipse里.java可以编译但不能运行??
  12. 面试中精华,俺自己总结的
  13. 9000.消息中间件MQTT
  14. 分布式搜索elasticsearch 索引文档的增删改查 入门
  15. 台达编码器型号含义_编码器型号说明 编码器型号大全 编码器型号选型
  16. latex zip 数模模板_数学建模美赛LaTeX模板
  17. [re入门]音乐文件加密破解
  18. python大数据就业方向_大数据方向:就业主要从事哪些工作?
  19. 数组下标访问越界导致溢出1
  20. 51单片机 Proteus仿真 时钟 串口 发送时钟 整点报时

热门文章

  1. 马尔科夫决策过程(MDP):赌徒问题
  2. 算法:回溯解决电话拨号中的字母组合Letter Combinations of a Phone Number
  3. 贝叶斯决策中的两类错误率分析
  4. android公交车代码,android实现查询公交车还有几站的功能
  5. 广义注意力- saliency map 关注图、gaze、Att
  6. A Test Checklist
  7. ServletConfig对象--配置初始化参数以及获取初始化参数
  8. 提交不了_领导嘲讽程序员代码太过整洁,网友:太矫情,这种代码提交不了
  9. python字符串转换成数字_python – Jinja将字符串转换为整数
  10. c++关于函数的参数传递全部知识点详解