当前位置: 首页 > news >正文

揭秘CopyOnWriteArraySet:读多写少的并发集合利器

文章目录

    • 什么是CopyOnWriteArraySet?
    • 核心原理深度剖析
      • 底层数据结构
      • 写时复制机制详解
      • 迭代器的弱一致性
    • 与其它容器的性能对比
    • 实际应用场景
      • 1. 配置信息管理
      • 2. 在线用户状态收集器
      • 3. 事件监听器管理
    • 实战案例:高并发在线用户监测
    • 局限性及注意事项
    • 总结
    • 参考文章

大家好,我是你们的技术老友科威舟,今天给大家分享一下Java JUC包中的CopyOnWriteArraySet原理。

如何在高并发读取场景中既保证线程安全又提升性能?CopyOnWriteArraySet或许正是你要找的解决方案。

在多线程编程的世界里,线程安全始终是我们需要直面的挑战。当涉及到共享集合时,这种挑战尤为突出。今天,我们要深入剖析的是一位在并发编程中不可或缺的"特长生"——CopyOnWriteArraySet。

什么是CopyOnWriteArraySet?

简单来说,CopyOnWriteArraySet是Java并发包(java.util.concurrent)中提供的一个线程安全的Set实现。它与众不同之处在于采用了"写时复制"(Copy-On-Write)策略,这意味着每次修改操作(如添加、删除元素)都会创建底层数组的一个新副本,而读操作则直接在原数组上进行。

想象一下图书馆的管理方式:当有人只想查阅书籍(读操作)时,可以直接进入书库浏览;但当需要增加或下架书籍(写操作)时,管理员会先创建一个完整的书库副本,在副本上进行修改,最后再用修改后的副本替换原始书库。这样,查阅者永远不会被阻塞,但修改操作的成本较高。

核心原理深度剖析

底层数据结构

CopyOnWriteArraySet的巧妙之处在于其内部实现:它完全依赖CopyOnWriteArrayList作为其存储结构。

publicclassCopyOnWriteArraySet<E>extendsAbstractSet<E>implementsjava.io.Serializable{// 内部使用CopyOnWriteArrayList来存储元素privatefinalCopyOnWriteArrayList<E>al;publicCopyOnWriteArraySet(){al=newCopyOnWriteArrayList<E>();}}

这里就产生了一个有趣的问题:CopyOnWriteArrayList本身允许元素重复,而Set要求元素唯一。CopyOnWriteArraySet如何解决这一矛盾?

答案是它通过调用CopyOnWriteArrayList的addIfAbsent方法来确保元素的唯一性。

写时复制机制详解

添加元素的流程体现了写时复制的精髓:

publicbooleanadd(Ee){// 调用addIfAbsent方法,确保元素不存在时才添加returnal.addIfAbsent(e);}

addIfAbsent方法内部会先检查元素是否已存在,只有在元素不存在时才会执行添加操作。添加过程需要获取可重入锁,然后创建原数组的副本,在新副本上添加元素,最后将副本设置为新的当前数组。

这一过程保证了写操作的原子性和线程安全,但代价是每次写操作都需要复制整个数组,因此写操作的性能与数组大小成正比。

迭代器的弱一致性

CopyOnWriteArraySet的迭代器具有弱一致性特征,这意味着迭代器创建时会获取当前数组的快照,在迭代过程中不会反映其他线程对集合的修改。这避免了ConcurrentModificationException异常,但代价是可能遍历到过期的数据。

publicIterator<E>iterator(){// 返回一个基于当前数组快照的迭代器returnal.iterator();}

与其它容器的性能对比

为了更直观地理解CopyOnWriteArraySet的特性,我们通过一个表格比较它与其它常见容器的区别:

特性HashSetConcurrentHashMap.KeySetViewCopyOnWriteArraySet
读性能O(1)O(1)O(n)但无锁
写性能O(1)O(1)带锁竞争O(n)复制开销
内存消耗最低中等较高(写时复制)
迭代器一致性弱一致性弱一致性强一致性(快照)
适用场景单线程环境高频读写低频写+高频读

从对比中可以看出,CopyOnWriteArraySet在读多写少的场景中具有明显优势,特别是在需要频繁遍历且写操作较少的并发环境中。

实际应用场景

1. 配置信息管理

在大型系统中,配置信息通常只在启动时加载,之后会被多个线程频繁读取,但极少修改。使用CopyOnWriteArraySet存储配置项,可以保证在高并发读取时的性能。

publicclassConfigurationManager{privatefinalCopyOnWriteArraySet<ConfigListener>listeners=newCopyOnWriteArraySet<>();// 添加配置监听器(写操作少)publicvoidaddListener(ConfigListenerlistener){listeners.add(listener);}// 通知所有监听器(读操作多)publicvoidfireConfigChanged(ConfigEventevent){for(ConfigListenerlistener:listeners){listener.onConfigChanged(event);}}}

2. 在线用户状态收集器

电商平台需要实时跟踪在线用户状态,这类场景中读取频率远高于写入频率

publicclassOnlineUserManager{privatefinalCopyOnWriteArraySet<Long>onlineUsers=newCopyOnWriteArraySet<>();// 用户登录(写操作)publicbooleanuserLogin(LonguserId){booleanadded=onlineUsers.add(userId);if(added){log.info("用户{}登录成功,当前在线人数:{}",userId,onlineUsers.size());}returnadded;}// 获取在线用户列表(读操作)publicSet<Long>getOnlineUsers(){returnCollections.unmodifiableSet(onlineUsers);}}

3. 事件监听器管理

GUI框架或事件驱动系统中,监听器通常在初始化时注册,之后主要进行遍历操作以通知事件,这正是CopyOnWriteArraySet的用武之地。

实战案例:高并发在线用户监测

以下是一个基于Spring Boot的在线用户监测系统完整示例,展示了CopyOnWriteArraySet在生产环境中的应用:

@ComponentpublicclassOnlineUserManager{privatefinalCopyOnWriteArraySet<Long>onlineUsers=newCopyOnWriteArraySet<>();privatefinalConcurrentMap<Long,Long>lastHeartbeat=newConcurrentHashMap<>();privatestaticfinallongHEARTBEAT_TIMEOUT=300_000;// 5分钟// 用户登录publicbooleanuserLogin(LonguserId){booleanresult=onlineUsers.add(userId);if(result){lastHeartbeat.put(userId,System.currentTimeMillis());}returnresult;}// 心跳保活publicvoidrefreshHeartbeat(LonguserId){if(onlineUsers.contains(userId)){lastHeartbeat.put(userId,System.currentTimeMillis());}}// 定时清理超时用户publicvoidcleanExpiredUsers(){longnow=System.currentTimeMillis();for(LonguserId:onlineUsers){LonglastTime=lastHeartbeat.get(userId);if(lastTime!=null&&now-lastTime>HEARTBEAT_TIMEOUT){onlineUsers.remove(userId);lastHeartbeat.remove(userId);}}}}

这个实现能够支持5000+ QPS的并发读取压力,同时保证用户登录/登出操作的原子性。

局限性及注意事项

尽管CopyOnWriteArraySet在特定场景下表现优秀,但它并非万能钥匙,存在以下局限性:

  1. 内存开销大:写操作需要复制整个数组,内存占用较高
  2. 实时性弱:读操作可能无法立即看到其他线程的最新修改
  3. 写性能随数据量增长而下降:不适合存储大量数据且写操作频繁的场景

总结

CopyOnWriteArraySet是Java并发包中一颗璀璨的明珠,它在读多写少的并发场景中表现卓越。通过写时复制机制,它实现了读操作的无锁并发,特别适合配置管理、事件监听、会话管理等场景。

但是,选择数据结构时一定要根据实际应用场景权衡利弊。如果你的应用写操作频繁数据量巨大,那么ConcurrentHashMap.KeySetView可能是更好的选择。

技术选型的艺术不在于选择最先进的技术,而在于选择最合适的技术。CopyOnWriteArraySet的存在再次证明了这一原则:在正确的场景下,即使看似"低效"的复制整个数组的策略,也能成为解决高并发问题的利器。

参考文章

  1. [Java CopyOnWriteArraySet源码深度解析 - CSDN]
  2. [深度剖析 Java CopyOnWriteArraySet:源码级使用原理揭秘 - 51CTO]
  3. [揭秘 Java CopyOnWriteArraySet:深入源码剖析使用原理 - CSDN]
  4. [CopyOnWriteArraySet - CSDN]
  5. [JUC集合类 CopyOnWriteArraySet源码解析 JDK8 - CSDN]
  6. [基于CopyOnWriteArraySet的高并发在线用户状态收集器架构设计 - CSDN]
  7. [如何在Java中使用CopyOnWriteArraySet - PHP中文网]
  • 本文主要观点基于以上参考资料,结合实际开发经验整理而成。转载请注明出处。*

更多技术干货欢迎关注微信公众号科威舟的AI笔记~

【转载须知】:转载请注明原文出处及作者信息

http://www.jsqmd.com/news/167684/

相关文章:

  • 医疗动态风险评估用GNN稳住预警
  • Java计算机毕设之基于SpringBoot社区住户信息管理系统住户信息管理、楼栋信息管理、房屋租售管理、缴费信息管理、报修信息管理(完整前后端代码+说明文档+LW,调试定制等)
  • 浙江口碑好的港澳台联考教育机构哪家专业
  • 机械制图 - Ac1d
  • 学生党论文降AI率,前五工具够用了
  • 【计算机毕业设计案例】基于SpringBoot的发动机零件、电器元件汽车配件仓储管理系统设计与实现(程序+文档+讲解+定制)
  • 查重没问题却卡AI?前五降AI工具更稳
  • Linux中级の自动化运维工具Ansible变量
  • 论文降AI别瞎改,这五佳工具效率高
  • 2025 MBA必备!9大AI论文平台深度测评与推荐
  • 【课程设计/毕业设计】基于SpringBoot的汽车配件仓储管理配件档案 - 出入库管理 - 库存监控系统设计与实现【附源码、数据库、万字文档】
  • CH592F开发入门2
  • 儿童近视度数增长过快?眼调节训练灯破解“度数逐年递增”困扰!
  • 五佳降AI工具合集,速降到合格线内
  • 如何根据年龄选择适合的近视防控方法?科学分龄,精准防控!
  • VS Code调试Node.js核心 打断点 → F5 → 单步执行 + 看变量 - jerry
  • 详细解析 SYN泛洪
  • Java毕设项目:基于SpringBoot的汽车配件仓储管理系统设计与实现(源码+文档,讲解、调试运行,定制等)
  • 项目风险预警:智能分析系统
  • 性价比高的循环水处理口碑好的公司
  • 军用车辆检测数据集介绍-2274张图片 军事侦察监控 卫星图像分析 边境安全防护 军事装备管理 战场态势感知 安全威胁评估
  • 理解机器学习里的NFL定理
  • 五佳降AI工具分享,论文党必收藏
  • 进程,动态库,COM机制关系概述
  • Zed编辑器安装与使用Agent Servers(腾讯CodeBuddy、阿里百炼Qwen Code、DeepSeek Cli)
  • RAG学习笔记 检索优化
  • 【毕业设计】基于SpringBoot的汽车配件仓储管理系统设计与实现(源码+文档+远程调试,全bao定制等)
  • 降AI率不靠硬改,前五工具更省事
  • 使用illustrator的套索工具提取元素
  • Kingbase-KEMCC配置集群监控纳管ES以及外部备份使用对象存储