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

Java并发编程:CopyOnWriteArrayList与Collections.synchronizedList深度解析

Java并发编程:CopyOnWriteArrayList与Collections.synchronizedList深度解析

    • 前言
    • 一、基础认知:两个工具类的官方定位
      • 1.1 Collections.synchronizedList
      • 1.2 CopyOnWriteArrayList
    • 二、核心底层原理与执行流程
      • 2.1 Collections.synchronizedList 底层原理+执行流程图
        • 2.1.1 核心原理
        • 2.1.2 执行流程图
        • 2.1.3 关键源码
      • 2.2 CopyOnWriteArrayList 底层原理+执行流程图
        • 2.2.1 核心原理
        • 2.2.2 执行流程图
        • 2.2.3 关键源码
    • 三、CopyOnWriteArrayList和Collections.synchronizedList核心区别
    • 四、详细优缺点分析
      • 4.1 Collections.synchronizedList:优缺点
        • 4.1.1 优点
        • 4.1.2 缺点
      • 4.2 CopyOnWriteArrayList:优缺点
        • 4.2.1 优点
        • 4.2.2 缺点
    • 五、关键特性补充
      • 5.1 迭代器机制区别
      • 5.2 数据一致性区别
    • 六、使用场景推荐
      • 6.1 优先使用 Collections.synchronizedList 的场景
      • 6.2 优先使用 CopyOnWriteArrayList 的场景
    • 七、代码示例
      • 7.1 Collections.synchronizedList 使用示例
      • 7.2 CopyOnWriteArrayList 使用示例
    • 八、总结
    • 结尾
      • 关键点回顾

🌺The Begin🌺点点关注,收藏不迷路🌺

前言

在Java多线程开发中,线程安全的List集合是高频使用的工具类。ArrayList是非线程安全的,在并发场景下直接使用会抛出ConcurrentModificationException异常。JDK为我们提供了两种线程安全的List解决方案:Collections.synchronizedListCopyOnWriteArrayList

很多开发者对二者的底层原理、核心区别、适用场景模糊不清,本文将从底层实现、读写机制、性能、优缺点、使用场景全方位对比,搭配流程图和源码解析,帮你彻底掌握二者的使用逻辑。


一、基础认知:两个工具类的官方定位

1.1 Collections.synchronizedList

  • 归属:java.util.Collections工具类的静态方法
  • 本质:基于synchronized关键字的装饰器模式,对普通List(如ArrayList)进行包装,给所有集合方法(add、get、remove、iterator等)加上同步锁,保证同一时间只有一个线程操作集合。
  • 线程安全方式:粗粒度锁,独占式同步

1.2 CopyOnWriteArrayList

  • 归属:java.util.concurrent并发包(JUC)
  • 本质:写时复制机制的线程安全List,是JDK专为高并发读场景设计的集合。
  • 核心思想:读操作无锁,写操作时复制一份新数组,修改完成后替换原数组
  • 线程安全方式:无锁读+独占锁写,读写分离

二、核心底层原理与执行流程

2.1 Collections.synchronizedList 底层原理+执行流程图

2.1.1 核心原理

通过互斥锁(synchronized)序列化所有对集合的操作,无论读还是写,所有线程必须竞争同一把锁,只有获取锁的线程才能执行操作,其他线程阻塞等待。

2.1.2 执行流程图

线程1获取锁

其他线程

多线程并发访问synchronizedList

线程竞争锁

执行读/写操作

释放锁

阻塞等待锁

锁释放后重新竞争

操作完成

2.1.3 关键源码
// 包装普通List,所有方法加synchronizedpublicstatic<T>List<T>synchronizedList(List<T>list){returnnewSynchronizedList<>(list);}// 核心方法:add/get/remove都加锁publicEget(intindex){synchronized(mutex){returnlist.get(index);}}publicbooleanadd(Ee){synchronized(mutex){returnlist.add(e);}}

2.2 CopyOnWriteArrayList 底层原理+执行流程图

2.2.1 核心原理
  1. 读操作:直接访问原数组,无锁、无阻塞
  2. 写操作:加ReentrantLock独占锁,复制一个新数组,在新数组上修改,修改完成后将原数组引用指向新数组;
  3. 读写分离:读操作不加锁,写操作加锁,读和写可以同时执行。
2.2.2 执行流程图

读操作

写操作

多线程并发访问CopyOnWriteArrayList

操作类型

直接读取原数组 无锁

获取ReentrantLock锁

复制新数组

在新数组执行修改

将原数组指向新数组

释放锁

操作完成

2.2.3 关键源码
// 读操作:无锁publicEget(intindex){returnget(getArray(),index);}// 写操作:加锁+复制数组publicbooleanadd(Ee){finalReentrantLocklock=this.lock;lock.lock();// 加锁try{Object[]elements=getArray();intlen=elements.length;// 复制新数组Object[]newElements=Arrays.copyOf(elements,len+1);newElements[len]=e;setArray(newElements);// 替换原数组returntrue;}finally{lock.unlock();// 释放锁}}

三、CopyOnWriteArrayList和Collections.synchronizedList核心区别

对比维度Collections.synchronizedListCopyOnWriteArrayList
1. 实现方式synchronized关键字独占锁写时复制(CopyOnWrite)+ ReentrantLock
2. 锁粒度粗粒度(所有操作共用一把锁)细粒度(仅写操作加锁)
3. 读写机制读写都加锁,串行执行读无锁,写加锁,读写并行
4. 迭代器快速失败(fail-fast),迭代时修改抛异常安全失败(fail-safe),迭代时修改不抛异常
5. 数据一致性强一致性(读写都能看到最新数据)最终一致性(读可能读到旧数据)
6. 内存占用低(无额外数组复制)高(写操作会复制新数组)
7. 适用场景读写均衡、写多读少、强一致性要求读多写少、高并发读、弱一致性可接受

四、详细优缺点分析

4.1 Collections.synchronizedList:优缺点

4.1.1 优点
  1. 内存占用低:无需复制数组,不会产生内存冗余,适合大数据量集合;
  2. 数据强一致:读写都加锁,任何操作都能获取最新数据,一致性要求高的场景首选;
  3. 使用简单:一行代码即可包装普通List,入门成本低;
  4. 写性能稳定:写操作无需复制数组,大集合写操作效率高于CopyOnWriteArrayList。
4.1.2 缺点
  1. 并发性能差:读写都加锁,高并发读场景下大量线程阻塞,CPU利用率低;
  2. 迭代器不安全:迭代过程中集合被修改,直接抛出ConcurrentModificationException
  3. 锁竞争激烈:粗粒度锁,所有线程竞争同一把锁,并发能力弱。

4.2 CopyOnWriteArrayList:优缺点

4.2.1 优点
  1. 高并发读:读操作完全无锁,多线程可同时读取,并发读性能极致;
  2. 迭代器安全:安全失败机制,迭代时修改集合不抛异常;
  3. 无锁竞争:读操作无锁,只有写操作竞争锁,读多场景下无性能瓶颈;
  4. 线程安全可靠:JUC官方实现,稳定性强,无需手动加锁。
4.2.2 缺点
  1. 内存占用高:写操作复制新数组,大数据量下会频繁GC,内存压力大;
  2. 数据不一致:读操作可能读到旧数据,不适合强一致性场景;
  3. 写性能差:复制数组+加锁,频繁写操作效率极低;
  4. 功能限制:不支持批量写入等复杂高效写操作。

五、关键特性补充

5.1 迭代器机制区别

  1. Collections.synchronizedList:快速失败(fail-fast)
    迭代时会记录集合修改次数,一旦检测到修改,立即抛出异常,不支持并发迭代修改
  2. CopyOnWriteArrayList:安全失败(fail-safe)
    迭代时操作的是原数组快照,写操作修改的是新数组,彼此隔离,迭代时可安全修改集合

5.2 数据一致性区别

  1. synchronizedList:强一致性,线程修改后,其他线程立即可见;
  2. CopyOnWriteArrayList:最终一致性,写操作完成后,读线程才能看到新数据,中间存在短暂延迟。

六、使用场景推荐

6.1 优先使用 Collections.synchronizedList 的场景

  1. 读写操作比例均衡,没有明显的读多写少特征;
  2. 数据强一致性要求极高(如金融交易、库存扣减);
  3. 集合数据量极大,无法承受数组复制带来的内存开销;
  4. 写操作频繁,需要保证写操作性能。

6.2 优先使用 CopyOnWriteArrayList 的场景

  1. 高并发读、极少写(如配置列表、白名单、字典数据);
  2. 读性能要求远高于写性能;
  3. 允许数据短暂不一致,追求高并发;
  4. 需要在迭代过程中安全修改集合。

七、代码示例

7.1 Collections.synchronizedList 使用示例

importjava.util.ArrayList;importjava.util.Collections;importjava.util.List;publicclassSynchronizedListDemo{publicstaticvoidmain(String[]args){// 包装ArrayList,生成线程安全ListList<String>list=Collections.synchronizedList(newArrayList<>());// 多线程读写安全list.add("test");Stringvalue=list.get(0);System.out.println(value);}}

7.2 CopyOnWriteArrayList 使用示例

importjava.util.concurrent.CopyOnWriteArrayList;publicclassCopyOnWriteDemo{publicstaticvoidmain(String[]args){CopyOnWriteArrayList<String>list=newCopyOnWriteArrayList<>();// 读无锁,写加锁list.add("Java");Stringresult=list.get(0);System.out.println(result);// 迭代时修改,不抛异常for(Strings:list){list.add("并发编程");}}}

八、总结

  1. 底层核心:synchronizedList是锁所有操作,CopyOnWriteArrayList是读写分离、写时复制
  2. 性能关键:读多写少用CopyOnWriteArrayList,读写均衡/写多读少用synchronizedList;
  3. 一致性:synchronizedList强一致,CopyOnWriteArrayList最终一致;
  4. 内存:synchronizedList低内存,CopyOnWriteArrayList高内存;
  5. 选型口诀:读多选COW,读写均衡选同步,强一致选同步,高并发读选COW。

结尾

本文详细对比了Java中两种线程安全List的底层原理、核心区别、优缺点和使用场景,希望能帮助大家在实际开发中精准选型,避免因集合选型不当导致的并发问题或性能瓶颈。

如果本文对你有帮助,欢迎点赞、收藏、关注,后续会持续更新Java并发编程干货!


关键点回顾

  1. Collections.synchronizedListsynchronized粗粒度锁,读写都加锁,强一致性,内存高效。
  2. CopyOnWriteArrayList:写时复制,读无锁写加锁,读写分离,最终一致性,高并发读。
  3. 核心选型依据读写比例 + 一致性要求 + 内存限制
  4. 迭代器:同步List快速失败,COWList安全失败。


🌺The End🌺点点关注,收藏不迷路🌺
http://www.cnnetsun.cn/news/2585583.html

相关文章:

  • 部署 - 问题:无法访问对应文件,请添加MIME映射
  • LeetCode刷题 day20
  • 全覆盖通讯导航测风雷达:野外风电应用方案
  • DIY便携式三路数控开关电源:从原理到实践的全流程解析
  • 5分钟从零开始:Translumo实时屏幕翻译工具完整使用指南
  • Windows 安装 MySQL 8 和 DBeaver
  • AI代码助手实战:从零重构磁悬浮控制程序
  • PyTorch、TensorFlow、Keras框架选型实战指南
  • 3步禁用Windows Defender:通过WSC API实现安全中心管理的技术解析
  • 普通Java程序员如何快速上手性能优化?
  • 基于CD4053的ATtiny编程切换器设计:告别反复插拔,提升开发效率
  • 从Perl到天气预报:手把手教你用SPEC CPU 2017在Ubuntu 22.04上跑通所有43个测试
  • Win11Debloat:3步告别Windows臃肿,重获系统纯净与性能
  • 无监督域适应:用合成数据训练6D姿态估计模型的实战指南
  • 数据库数据类型选型实战:精度、时区与跨库兼容性指南
  • Python-CAN实战:从零构建一个CAN总线数据监控与分析工具
  • WinThumbsPreloader-V2:告别Windows图片文件夹加载卡顿的终极解决方案
  • Creao 三位创始人谈 Harness 工程:AI 主导开发,六周工作一天完成,企业转型挑战几何?
  • ARM架构SError异常机制与RAS特性解析
  • Maven install Java.lang.StackOverflowError
  • AI大模型中常见的20个基础概念,建议收藏!
  • 轻松解决验证码难题的5种方法
  • 打工人必看:用大模型提效的5个技巧,每天多出2小时
  • DFS岛屿问题:核心思想与实战模板
  • Switch游戏文件编辑完全手册:专业级工具深度解析与实战指南
  • 【卫星】基于matlab卫星星座的红外跟踪可配置弹道导弹轨迹,从地球上任何起点和目的地【含Matlab源码 15670期】
  • Lovable数据分析平台落地全周期拆解(从零部署到高阶建模全流程)
  • 免费CRM系统有哪些?一文分清真假免费,中小企业零成本选型攻略
  • LNLF-BERT:基于双层级注意力机制的长文本处理模型解析与实践
  • 场感知矩阵分解:从传统协同过滤到上下文感知推荐的跃迁