尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

实测对比:CopyOnWriteArrayList 与 SynchronizedList 并发性能,结果颠覆认知! 前言在 Java 并发编程中线程安全的 List 集合是高频使用的组件其中CopyOnWriteArrayList和Collections.synchronizedList最具代表性。很多开发者只知其线程安全却不清楚二者在并发读写场景下的性能差距。本文通过可运行的 SpringBoot 测试代码真实对比两者的并发读写性能用数据告诉你什么场景选哪个集合才是最优解一、核心概念先科普CopyOnWriteArrayListJUC 包下的写时复制集合写操作会复制整个数组读操作无锁适用于读多写少场景。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 并发性能测试 */RestControllerSlf4jpublicclassListConcurrentTestController{// 测试并发写性能GetMapping(write)publicMaptestWrite(){ListIntegercopyOnWriteArrayListnewCopyOnWriteArrayList();ListIntegersynchronizedListCollections.synchronizedList(newArrayList());StopWatchstopWatchnewStopWatch();intloopCount100000;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());MapresultnewHashMap();result.put(copyOnWriteArrayList,copyOnWriteArrayList.size());result.put(synchronizedList,synchronizedList.size());returnresult;}// 填充数据privatevoidaddAll(ListIntegerlist){list.addAll(IntStream.rangeClosed(1,1000000).boxed().collect(Collectors.toList()));}// 测试并发读性能GetMapping(read)publicMaptestRead(){ListIntegercopyOnWriteArrayListnewCopyOnWriteArrayList();ListIntegersynchronizedListCollections.synchronizedList(newArrayList());addAll(copyOnWriteArrayList);addAll(synchronizedList);StopWatchstopWatchnewStopWatch();intloopCount1000000;intcountcopyOnWriteArrayList.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());MapresultnewHashMap();result.put(copyOnWriteArrayList,copyOnWriteArrayList.size());result.put(synchronizedList,synchronizedList.size());returnresult;}}三、启动与测试方式编写 SpringBoot 启动类运行main方法启动项目浏览器访问接口查看性能结果并发写测试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无锁设计优势拉满原始代码问题总结无JVM预热JIT编译、类加载会严重干扰测试结果测试环境不隔离串行测试导致结果不公平魔法值泛滥代码可读性、可维护性差无泛型规范类型不安全并发不可控依赖公共并行流线程池结果波动大五、性能差距核心原因CopyOnWriteArrayList 写慢的本质每次添加/修改元素都会复制整个底层数组数据量越大复制开销越大并发写性能极差。SynchronizedList 写快的本质仅通过synchronized加锁保证线程安全无需复制数组锁竞争开销远小于数组复制。CopyOnWriteArrayList 读快的本质读操作完全无锁多线程可同时读取没有任何锁竞争开销。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 并发性能对比 */RestControllerSlf4jpublicclassOptimizedListTestController{// 常量定义避免魔法值privatestaticfinalintWRITE_TIMES50000;privatestaticfinalintREAD_TIMES500000;privatestaticfinalintDATA_SIZE500000;// 固定线程池控制并发度避免公共线程池干扰privatestaticfinalExecutorServiceEXECUTORExecutors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());/** * 优化独立测试写性能预热 独立环境 多次执行 */GetMapping(optimized/write)publicMapString,ObjecttestOptimizedWrite(){// 预热 JVMwarmUp();// 测试 CopyOnWriteArrayListListIntegercowanewCopyOnWriteArrayList();StopWatchsw1newStopWatch();sw1.start(CopyOnWriteArrayList-写);executeConcurrentWrite(cowa);sw1.stop();// 测试 SynchronizedListListIntegerslCollections.synchronizedList(newArrayList());StopWatchsw2newStopWatch();sw2.start(SynchronizedList-写);executeConcurrentWrite(sl);sw2.stop();log.info( 写性能测试结果 );log.info(sw1.prettyPrint());log.info(sw2.prettyPrint());MapString,ObjectresultnewHashMap();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)publicMapString,ObjecttestOptimizedRead(){warmUp();// 初始化数据ListIntegercowanewCopyOnWriteArrayList();ListIntegerslCollections.synchronizedList(newArrayList());initData(cowa);initData(sl);// 读测试StopWatchsw1newStopWatch();sw1.start(CopyOnWriteArrayList-读);executeConcurrentRead(cowa);sw1.stop();StopWatchsw2newStopWatch();sw2.start(SynchronizedList-读);executeConcurrentRead(sl);sw2.stop();log.info( 读性能测试结果 );log.info(sw1.prettyPrint());log.info(sw2.prettyPrint());MapString,ObjectresultnewHashMap();result.put(cowa_read_time_ms,sw1.getTotalTimeMillis());result.put(sl_read_time_ms,sw2.getTotalTimeMillis());returnresult;}// JVM 预热消除编译、加载影响privatevoidwarmUp(){ListIntegertempnewArrayList();IntStream.range(0,10000).parallel().forEach(i-temp.add(i));temp.clear();log.info(JVM 预热完成);}// 统一写逻辑privatevoidexecuteConcurrentWrite(ListIntegerlist){IntStream.range(0,WRITE_TIMES).parallel().forEach(i-list.add(ThreadLocalRandom.current().nextInt(WRITE_TIMES)));}// 统一读逻辑privatevoidexecuteConcurrentRead(ListIntegerlist){intsizelist.size();IntStream.range(0,READ_TIMES).parallel().forEach(i-list.get(ThreadLocalRandom.current().nextInt(size)));}// 初始化数据privatevoidinitData(ListIntegerlist){list.addAll(IntStream.rangeClosed(1,DATA_SIZE).boxed().collect(Collectors.toList()));}}测试访问地址优化版写测试http://localhost:8080/optimized/write优化版读测试http://localhost:8080/optimized/read优化版核心改进点JVM预热消除编译器、类加载对测试结果的影响环境隔离读写测试完全独立互不干扰独立计时器每个集合单独计时统计更精准常量管理移除魔法值代码更规范固定线程池控制并发度测试结果稳定可复现标准泛型代码更安全、更优雅七、生产环境使用建议干货集合类型适用场景禁止场景CopyOnWriteArrayList读多写少、数据量小配置/白名单频繁写入、大数据量集合Collections.synchronizedList读写均衡、写多读少、强一致性超高并发读场景核心口诀读多写少选COW写多读少用同步锁八、总结本次测试通过真实并发场景验证了两种线程安全List的性能差异结果具有实战参考意义CopyOnWriteArrayList读极快、写极慢SynchronizedList读写均衡生产环境切勿盲目使用场景匹配才是最高效的选择性能测试一定要做JVM预热、隔离测试环境才能保证结果准确可信。
返回列表