0
点赞
收藏
分享

微信扫一扫

图解CopyOnWriteArraySet数据结构设计与应用案例

在这里插入图片描述

CopyOnWriteArraySet 是 Java 中的一个线程安全的集合类,它继承自 CopyOnWriteArrayList 并实现了 Set 接口。这个集合使用数组来存储元素,并且采用了“写时复制”(Copy-On-Write)的策略来保证线程安全。在进行修改操作(如添加或删除元素)时,CopyOnWriteArraySet 会复制整个底层数组,而不是直接在原数组上进行修改,这样可以避免使用锁,从而提高读操作的性能。由于写操作涉及数组的复制,所以当集合元素较多时,可能会影响性能。CopyOnWriteArraySet 适用于读操作远多于写操作的场景,例如并发缓存、实时查询结果的存储等。

肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 'mvcc' 获得手写数据库事务代码

欢迎 点赞,关注,评论。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

  • 解锁大语言模型参数:零基础掌握大型语言模型参数奥秘与实践指南
  • 高性能连接池之HikariCP框架分析:高性能逐条分解(架构师篇)
  • 缓存雪崩/穿透/击穿/失效原理图/14种缓存数据特征+10种数据一致性方案
  • Java 8函数式编程全攻略:43种函数式业务代码实战案例解析(收藏版)
  • 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
  • 17个Mybatis Plugs注解:Mybatis Plugs插件架构设计与注解案例(必须收藏)

1、 CopyOnWriteArraySet

CopyOnWriteArraySet 是 Java 并发包 java.util.concurrent 中的一个集合类,它是 CopyOnWriteArrayList 的一个变体,用于维护一个线程安全的、动态的元素集合。以下是 CopyOnWriteArraySet 的设计:

设计思考:
  1. 需求场景
    • 在多线程环境中,读操作频繁而写操作相对较少的场景,如缓存、实时数据集、事件监听器集合等。
  2. 现有技术局限性
    • 传统的线程安全集合,如 Collections.synchronizedSetHashtable,在读多写少的场景下可能因为写操作导致的线程阻塞,影响性能。
  3. 技术融合
    • CopyOnWriteArraySet 采用了写时复制(Copy-On-Write)的策略,在读操作远多于写操作的情况下,提高了读操作的性能。
  4. 设计理念
    • CopyOnWriteArraySet 利用了读操作远多于写操作的特点,在读操作时不需要加锁,从而提高了并发读的性能。
  5. 实现方式
    • 内部使用 CopyOnWriteArrayList 存储元素,所有写操作(添加、删除等)都会创建一个新的数组副本,然后对新数组进行修改,最后原子性地将内部数组引用指向新数组。
2、 数据结构

在这里插入图片描述

图说明
  • CopyOnWriteArraySet:表示 CopyOnWriteArraySet 类的实例,它提供了线程安全的 Set 集合功能。
  • CopyOnWriteArrayList:CopyOnWriteArraySet 基于 CopyOnWriteArrayList 实现,后者是线程安全的 ArrayList 实现。
  • 内部数组:CopyOnWriteArrayList 初始的内部数组,用于存储集合中的元素。
  • 内部数组副本:当执行写操作(如添加、删除)时,CopyOnWriteArrayList 创建内部数组的一个副本。
  • 新内部数组:写操作完成后,CopyOnWriteArrayList 将内部数组引用指向新的数组副本。
  • 元素1、元素2、元素n:表示存储在 CopyOnWriteArraySet 中的实际数据。
3、 执行流程

在这里插入图片描述

图说明
  • 创建 CopyOnWriteArraySet 实例:初始化 CopyOnWriteArraySet 对象。
  • 添加元素:将元素添加到 CopyOnWriteArraySet。
  • 复制旧内部数组:为了添加元素,先复制当前的内部数组。
  • 修改新内部数组副本:在数组的副本上添加元素。
  • 将新内部数组副本设置为当前:将修改后的数组副本设置为当前数组,完成添加操作。
  • 删除元素:从 CopyOnWriteArraySet 删除元素。
  • 复制旧内部数组:为了删除元素,先复制当前的内部数组。
  • 修改新内部数组副本:在数组的副本上删除元素。
  • 将新内部数组副本设置为当前:将修改后的数组副本设置为当前数组,完成删除操作。
  • 遍历 CopyOnWriteArraySet:遍历 CopyOnWriteArraySet 中的所有元素。
  • 读取当前内部数组:读取当前的内部数组,进行遍历操作。
4、优点:
  1. 读操作性能高
    • 读操作不需要加锁,可以由多个线程并发进行。
  2. 线程安全
    • 写操作通过复制整个数组来保证线程安全。
  3. 写操作不阻塞读操作
    • 写操作不会影响读操作的执行,因为读操作作用于原数组。
5、 缺点:
  1. 写操作性能开销
    • 写操作需要复制整个数组,对于大数据量的数组,性能开销较大。
  2. 内存消耗
    • 写操作时会创建新的数组,可能会导致内存消耗较高。
  3. 数据实时性
    • 读操作可能读取到的不是最新数据,因为写操作完成后,数据的变更才会被复制到新数组。
6、使用场景:
  • 适用于读多写少的场景,如配置信息的存储、统计信息的收集、事件监听器的管理等。

7、类设计

在这里插入图片描述

8、应用案例

CopyOnWriteArraySet 通常用于实现线程安全的集合,特别是在读操作远多于写操作的情况下。这是一个用户会话管理器,用于跟踪用户的登录状态和最后活跃时间:

import java.util.concurrent.CopyOnWriteArraySet;

// 用户类,用于表示系统中的用户
class User {
private String id;
private String username;
private long lastActiveTime;

public User(String id, String username, long lastActiveTime) {
this.id = id;
this.username = username;
this.lastActiveTime = lastActiveTime;
}

// 省略 getter 和 setter 方法
@Override
public String toString() {
return User{ +
id=' + id + ''' +
, username=' + username + ''' +
, lastActiveTime= + lastActiveTime +
'}';
}
}

// 用户会话管理器类
class UserSessionManager {
private CopyOnWriteArraySet<User> activeUsers;

public UserSessionManager() {
activeUsers = new CopyOnWriteArraySet<>();
}

// 添加或更新用户会话
public void addUser(User user) {
activeUsers.add(user);
}

// 获取所有活跃用户
public CopyOnWriteArraySet<User> getActiveUsers() {
return activeUsers;
}

// 移除用户会话
public void removeUser(String userId) {
// 遍历 CopyOnWriteArraySet 以找到并移除指定用户
for (User user : activeUsers) {
if (user.getId().equals(userId)) {
activeUsers.remove(user);
break;
}
}
}
}

public class Main {
public static void main(String[] args) {
UserSessionManager sessionManager = new UserSessionManager();

// 模拟用户登录
sessionManager.addUser(new User(1, Alice, System.currentTimeMillis()));
sessionManager.addUser(new User(2, Bob, System.currentTimeMillis()));

// 获取并打印所有活跃用户
CopyOnWriteArraySet<User> activeUsers = sessionManager.getActiveUsers();
for (User user : activeUsers) {
System.out.println(Active User: + user);
}

// 模拟用户注销
sessionManager.removeUser(1);

// 再次获取并打印所有活跃用户
activeUsers = sessionManager.getActiveUsers();
for (User user : activeUsers) {
System.out.println(Active User: + user);
}
}
}
举报

相关推荐

0 条评论