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

Java高频面试之集合-08

hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶

面试官:详细说说CopyOnWriteArrayList


CopyOnWriteArrayList 详解

CopyOnWriteArrayList 是 Java 并发包(java.util.concurrent)中提供的线程安全列表,基于“写时复制”(Copy-On-Write)机制实现。它适用于读多写少的高并发场景,如事件监听器列表、配置管理等。


核心特性
特性说明
线程安全读操作无锁,写操作通过锁保证线程安全。
数据一致性读操作基于快照,迭代期间不会抛出 ConcurrentModificationException
写开销每次修改操作会复制底层数组,内存占用较高。
适用场景读操作频繁,写操作极少(如监听器管理、配置存储)。

底层实现
  1. 数据结构

    • 基于动态数组(volatile Object[] array)存储数据。
    • 所有读操作直接访问当前数组,无需同步。
  2. 写时复制(COW)

    • 修改操作流程
      1. 加锁(ReentrantLock)。
      2. 复制原数组,生成新数组。
      3. 在新数组上执行修改操作。
      4. 将底层数组引用指向新数组(setArray(newArray))。
      5. 释放锁。
    • 代码示例(add 方法)
      public boolean add(E e) {
          final ReentrantLock lock = this.lock;
          lock.lock();
          try {
              Object[] elements = getArray();
              int len = elements.length;
              Object[] newElements = Arrays.copyOf(elements, len + 1);
              newElements[len] = e;
              setArray(newElements);
              return true;
          } finally {
              lock.unlock();
          }
      }
      
  3. 迭代器

    • 基于迭代器创建时的数组快照遍历数据。
    • 不支持修改操作(如 removeset),调用会抛出 UnsupportedOperationException
    • 代码示例
      public Iterator<E> iterator() {
          return new COWIterator<E>(getArray(), 0);
      }
      

优点与缺点
优点缺点
读操作无锁,性能极高。写操作内存开销大(复制全量数据)。
避免并发修改异常(ConcurrentModificationException)。数据弱一致性(读操作可能不反映最新状态)。
实现简单,适合读多写少场景。写操作频繁时性能急剧下降。

适用场景
  1. 监听器列表
    如 GUI 事件监听器,注册后极少修改,但频繁触发事件(读)。

    // 添加监听器(写操作少)
    listeners.add(new Listener());
    
    // 触发事件(读操作多)
    for (Listener listener : listeners) {
        listener.onEvent();
    }
    
  2. 配置管理
    系统配置通常加载后很少修改,但频繁读取。

    CopyOnWriteArrayList<Config> configs = loadConfigs();
    // 读取配置(无锁)
    String value = configs.get(0).getProperty("key");
    
  3. 缓存快照
    需要缓存某个时间点的数据快照供查询。


性能对比
操作ArrayListCopyOnWriteArrayListCollections.synchronizedList
读(单线程)O(1)(最快)O(1)(无锁,快)O(1)(同步开销,较慢)
读(高并发)非线程安全,需外部同步O(1)(无锁,最快)O(1)(同步开销,慢)
写(单线程)O(1)(快)O(n)(复制数组,慢)O(1)(同步开销,较慢)
写(高并发)非线程安全O(n)(锁竞争,最慢)O(1)(同步开销,慢)

注意事项
  1. 避免频繁写操作
    批量写入时,优先使用 addAll 代替多次 add,减少数组复制次数。

    // 不推荐
    for (String item : items) {
        list.add(item);
    }
    
    // 推荐
    list.addAll(items);
    
  2. 迭代器弱一致性
    迭代器遍历的是创建时的快照,可能无法感知后续修改。

    CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3));
    Iterator<Integer> it = list.iterator();
    list.add(4);
    while (it.hasNext()) {
        System.out.print(it.next()); // 输出 1,2,3(不包含4)
    }
    
  3. 内存监控
    大对象或超大数组可能导致内存压力,需监控堆内存使用。


🐮🐎
  • 使用场景:读多写少,允许数据弱一致性。
  • 替代方案
    • 写多读少:考虑 ConcurrentLinkedQueueConcurrentHashMap
    • 强一致性需求:使用锁或 synchronizedList
  • 最佳实践:结合业务特点选择数据结构,必要时进行性能压测。

在这里插入图片描述

相关文章:

  • STM32上跑SimpleFOC,电流环、速度环、位置环、棘轮软硬件全开源
  • WPF在特定领域的应用:打造一款专业的图像编辑工具
  • 检索增强生成(RAG)、微调(Fine-tuning)与知识蒸馏(Knowledge Distillation):核心差异与技术选型指南
  • 管理网络安全
  • python collections库速查
  • 订单回款自动化,实现高效运营
  • git常用操作
  • 重磅推出四合一镜像站,免废使用
  • 人形机器人---越来越像人了
  • C++ String类
  • 华为机试牛客刷题之HJ14 字符串排序
  • 通过数据集微调LLM后怎么调用
  • 手写 Promise 的实现
  • Redis7系列:设置开机自启
  • 贪心算法三
  • 3月09日奇怪的Incorrect datetime value
  • git worktree的使用
  • c语言笔记 内存管理之栈内存
  • npm终端执行时报错
  • XGBoost介绍
  • 国家消防救援局应急通信和科技司负责人张昊接受审查调查
  • “80后”湖南岳阳临湘市市长刘琦任临湘市委书记
  • 新华社原香港分社副社长、深圳市委原副书记秦文俊逝世
  • 特朗普与泽连斯基通话
  • 中国预警机雷达有多强?可数百公里外看清足球轨迹
  • 和平会谈两天后,俄对乌发动冲突爆发以来最大规模无人机袭击