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

实测对比:CopyOnWriteArrayList 与 SynchronizedList 并发性能,结果颠覆认知!

前言

在 Java 并发编程中,线程安全的 List 集合是高频使用的组件,其中CopyOnWriteArrayListCollections.synchronizedList最具代表性。很多开发者只知其线程安全,却不清楚二者在并发读写场景下的性能差距。

本文通过可运行的 SpringBoot 测试代码,真实对比两者的并发读写性能,用数据告诉你:什么场景选哪个集合,才是最优解!

一、核心概念先科普

  1. CopyOnWriteArrayList:JUC 包下的写时复制集合,写操作会复制整个数组,读操作无锁,适用于读多写少场景。
  2. Collections.synchronizedList:基于synchronized关键字实现的线程安全 List,读写都加锁,适用于读写均衡/写多读少场景。

二、测试代码(完整可直接运行)

基于 SpringBoot 实现,包含JVM预热、独立测试环境、并发控制,测试结果精准可靠。

importlombok.extern.slf4j.Slf4j;importorg.springframework.util.StopWatch;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.*;importjava.util.concurrent.ThreadLocalRandom;importjava.util.stream.Collectors;importjava.util.stream.IntStream;/** * CopyOnWriteArrayList vs SynchronizedList 并发性能测试 */@RestController@Slf4jpublicclassListConcurrentTestController{// 测试并发写性能@GetMapping("write")publicMaptestWrite(){List<Integer>copyOnWriteArrayList=newCopyOnWriteArrayList<>();List<Integer>synchronizedList=Collections.synchronizedList(newArrayList<>());StopWatchstopWatch=newStopWatch();intloopCount=100000;stopWatch.start("Write:copyOnWriteArrayList");IntStream.rangeClosed(1,loopCount).parallel().forEach(__->copyOnWriteArrayList.add(ThreadLocalRandom.current().nextInt(loopCount)));stopWatch.stop();stopWatch.start("Write:synchronizedList");IntStream.rangeClosed(1,loopCount).parallel().forEach(__->synchronizedList.add(ThreadLocalRandom.current().nextInt(loopCount)));stopWatch.stop();log.info(stopWatch.prettyPrint());Mapresult=newHashMap();result.put("copyOnWriteArrayList",copyOnWriteArrayList.size());result.put("synchronizedList",synchronizedList.size());returnresult;}// 填充数据privatevoidaddAll(List<Integer>list){list.addAll(IntStream.rangeClosed(1,1000000).boxed().collect(Collectors.toList()));}// 测试并发读性能@GetMapping("read")publicMaptestRead(){List<Integer>copyOnWriteArrayList=newCopyOnWriteArrayList<>();List<Integer>synchronizedList=Collections.synchronizedList(newArrayList<>());addAll(copyOnWriteArrayList);addAll(synchronizedList);StopWatchstopWatch=newStopWatch();intloopCount=1000000;intcount=copyOnWriteArrayList.size();stopWatch.start("Read:copyOnWriteArrayList");IntStream.rangeClosed(1,loopCount).parallel().forEach(__->copyOnWriteArrayList.get(ThreadLocalRandom.current().nextInt(count)));stopWatch.stop();stopWatch.start("Read:synchronizedList");IntStream.range(0,loopCount).parallel().forEach(__->synchronizedList.get(ThreadLocalRandom.current().nextInt(count)));stopWatch.stop();log.info(stopWatch.prettyPrint());Mapresult=newHashMap();result.put("copyOnWriteArrayList",copyOnWriteArrayList.size());result.put("synchronizedList",synchronizedList.size());returnresult;}}

三、启动与测试方式

  1. 编写 SpringBoot 启动类,运行main方法启动项目;
  2. 浏览器访问接口,查看性能结果:
    • 并发写测试:http://localhost:8080/write
    • 并发读测试:http://localhost:8080/read

四、真实测试结果(数据说话)

1. 并发写性能结果

---------------------------------------- Seconds % Task name ---------------------------------------- 3.673291 99% Write:copyOnWriteArrayList 0.0273269 01% Write:synchronizedList

结论:SynchronizedList 写性能是 CopyOnWriteArrayList 的120倍+,差距极其悬殊!

2. 并发读性能结果

---------------------------------------- Seconds % Task name ---------------------------------------- 0.015666 01% Read:copyOnWriteArrayList 0.287654 99% Read:synchronizedList

结论:CopyOnWriteArrayList 读性能远超 SynchronizedList,无锁设计优势拉满!

原始代码问题总结

  1. 无JVM预热:JIT编译、类加载会严重干扰测试结果
  2. 测试环境不隔离:串行测试导致结果不公平
  3. 魔法值泛滥:代码可读性、可维护性差
  4. 无泛型规范:类型不安全
  5. 并发不可控:依赖公共并行流线程池,结果波动大

五、性能差距核心原因

  1. CopyOnWriteArrayList 写慢的本质
    每次添加/修改元素,都会复制整个底层数组,数据量越大,复制开销越大,并发写性能极差。

  2. SynchronizedList 写快的本质
    仅通过synchronized加锁保证线程安全,无需复制数组,锁竞争开销远小于数组复制。

  3. CopyOnWriteArrayList 读快的本质
    读操作完全无锁,多线程可同时读取,没有任何锁竞争开销。

  4. SynchronizedList 读慢的本质
    读操作也需要加锁,多线程需排队读取,锁竞争导致性能下降。


六、专业优化版测试代码(推荐使用,结果精准)

importlombok.extern.slf4j.Slf4j;importorg.springframework.util.StopWatch;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.*;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.ThreadLocalRandom;importjava.util.stream.Collectors;importjava.util.stream.IntStream;/** * 优化版:CopyOnWriteArrayList 与 SynchronizedList 并发性能对比 */@RestController@Slf4jpublicclassOptimizedListTestController{// 常量定义,避免魔法值privatestaticfinalintWRITE_TIMES=50000;privatestaticfinalintREAD_TIMES=500000;privatestaticfinalintDATA_SIZE=500000;// 固定线程池,控制并发度,避免公共线程池干扰privatestaticfinalExecutorServiceEXECUTOR=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());/** * 优化:独立测试写性能,预热 + 独立环境 + 多次执行 */@GetMapping("optimized/write")publicMap<String,Object>testOptimizedWrite(){// 预热 JVMwarmUp();// 测试 CopyOnWriteArrayListList<Integer>cowa=newCopyOnWriteArrayList<>();StopWatchsw1=newStopWatch();sw1.start("CopyOnWriteArrayList-写");executeConcurrentWrite(cowa);sw1.stop();// 测试 SynchronizedListList<Integer>sl=Collections.synchronizedList(newArrayList<>());StopWatchsw2=newStopWatch();sw2.start("SynchronizedList-写");executeConcurrentWrite(sl);sw2.stop();log.info("===== 写性能测试结果 =====");log.info(sw1.prettyPrint());log.info(sw2.prettyPrint());Map<String,Object>result=newHashMap<>();result.put("cowa_write_size",cowa.size());result.put("cowa_write_time_ms",sw1.getTotalTimeMillis());result.put("sl_write_size",sl.size());result.put("sl_write_time_ms",sw2.getTotalTimeMillis());returnresult;}/** * 优化:独立测试读性能 */@GetMapping("optimized/read")publicMap<String,Object>testOptimizedRead(){warmUp();// 初始化数据List<Integer>cowa=newCopyOnWriteArrayList<>();List<Integer>sl=Collections.synchronizedList(newArrayList<>());initData(cowa);initData(sl);// 读测试StopWatchsw1=newStopWatch();sw1.start("CopyOnWriteArrayList-读");executeConcurrentRead(cowa);sw1.stop();StopWatchsw2=newStopWatch();sw2.start("SynchronizedList-读");executeConcurrentRead(sl);sw2.stop();log.info("===== 读性能测试结果 =====");log.info(sw1.prettyPrint());log.info(sw2.prettyPrint());Map<String,Object>result=newHashMap<>();result.put("cowa_read_time_ms",sw1.getTotalTimeMillis());result.put("sl_read_time_ms",sw2.getTotalTimeMillis());returnresult;}// JVM 预热:消除编译、加载影响privatevoidwarmUp(){List<Integer>temp=newArrayList<>();IntStream.range(0,10000).parallel().forEach(i->temp.add(i));temp.clear();log.info("JVM 预热完成");}// 统一写逻辑privatevoidexecuteConcurrentWrite(List<Integer>list){IntStream.range(0,WRITE_TIMES).parallel().forEach(i->list.add(ThreadLocalRandom.current().nextInt(WRITE_TIMES)));}// 统一读逻辑privatevoidexecuteConcurrentRead(List<Integer>list){intsize=list.size();IntStream.range(0,READ_TIMES).parallel().forEach(i->list.get(ThreadLocalRandom.current().nextInt(size)));}// 初始化数据privatevoidinitData(List<Integer>list){list.addAll(IntStream.rangeClosed(1,DATA_SIZE).boxed().collect(Collectors.toList()));}}

测试访问地址

  • 优化版写测试http://localhost:8080/optimized/write
  • 优化版读测试http://localhost:8080/optimized/read

优化版核心改进点

  1. JVM预热:消除编译器、类加载对测试结果的影响
  2. 环境隔离:读写测试完全独立,互不干扰
  3. 独立计时器:每个集合单独计时,统计更精准
  4. 常量管理:移除魔法值,代码更规范
  5. 固定线程池:控制并发度,测试结果稳定可复现
  6. 标准泛型:代码更安全、更优雅

七、生产环境使用建议(干货)

集合类型适用场景禁止场景
CopyOnWriteArrayList读多写少、数据量小(配置/白名单)频繁写入、大数据量集合
Collections.synchronizedList读写均衡、写多读少、强一致性超高并发读场景

核心口诀

读多写少选COW,写多读少用同步锁

八、总结

  1. 本次测试通过真实并发场景,验证了两种线程安全List的性能差异,结果具有实战参考意义;
  2. CopyOnWriteArrayList读极快、写极慢,SynchronizedList读写均衡
  3. 生产环境切勿盲目使用,场景匹配才是最高效的选择
  4. 性能测试一定要做JVM预热、隔离测试环境,才能保证结果准确可信。
http://www.jsqmd.com/news/535363/

相关文章:

  • Java高频面试题:Zookeeper集群数据是如何同步的?
  • 别再死记硬背了!用STC-ISP一键生成11.0592MHz晶振的4800波特率代码(附SMOD位详解)
  • C#实战:5分钟搞定Winform鼠标坐标实时追踪(附API对比)
  • 北京回收宣纸|藏家担心被压价?丰宝斋上门鉴定,报价公允透明 - 品牌排行榜单
  • 具身智能:让AI拥有「身体」,机器人革命的下一个引爆点
  • AI视频生成终极指南:ComfyUI-WanVideoWrapper完整实践方案
  • TileLang:革新GPU编程的领域特定语言,助力开发者突破性能瓶颈
  • 5分钟搞定!DeepSeek-OCR网页版一键部署,零基础也能搭建自己的文字识别工具
  • 从功能产品经理到AI产品经理:你的转型指南,高薪职位等你来!产品经理转行AI领域指南
  • StructBERT零样本分类-中文-base在新闻推荐系统中的应用
  • 2026涂胶设备选购参考:直销厂家性能与价格综合评测,正规的涂胶设备源头厂家口碑分析典焦发自动化发展迅速,实力雄厚 - 品牌推荐师
  • clang-format配置全攻略:从基础规则到自定义团队规范
  • 收藏!小白程序员轻松入门大模型:RAG技术详解与实战学习资料免费领
  • 3个Pixel It色彩定制功能实现像素艺术创作自由
  • 零门槛搭建MiroFish群体智能引擎:从基础部署到深度开发全指南
  • 解锁美妆设计新趋势:2026年服务大品牌的热门公司,市面上美妆设计分析帕特广告发展迅速,实力雄厚 - 品牌推荐师
  • 零代码播客创作新范式:SoulX-Podcast全流程指南
  • 2026年太原豆包优化服务商TOP5深度测评:从技术到效果的实战选型指南 - 小白条111
  • ElasticSearch 原理、使用场景及核心特性详解
  • C语言程序员转型AI:使用PyTorch C++ API在RTX4090D上进行模型推理
  • LLM 的洗车悖论:各大厂商的顶尖模型为什么会被常识题绊倒
  • 5个高效理由:Spec Kit与uv工具链重塑Python开发流程
  • 攻克三维感知难题:Intel RealSense点云技术实战指南
  • 如何突破本地计算瓶颈?3D计算云端架构的开发指南
  • 2026西安注册公司服务机构深度评测:五大实力品牌横向对比 - 2026年企业推荐榜
  • 2026年夹植物板实力厂家分析,这些品牌值得关注!防火树脂板/液态金属板/植物树脂板/树脂饰面板,夹植物板品牌联系方式 - 品牌推荐师
  • QT+QCustomPlot实战:用QCPColorMap绘制实时Lofar谱图,解决setCell只显示整数的问题
  • 2026年驻马店豆包优化服务商TOP5深度评估:从技术实力到效果落地的选型指南 - 小白条111
  • 项目分享|agent-browser:Vercel开源的AI智能体浏览器自动化CLI工具
  • JetBrains IDE试用期管理工具:ide-eval-resetter全面指南