news 2026/5/25 15:00:30

Collections.synchronizedList、CopyOnWriteArrayList 和 synchronized对比

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Collections.synchronizedList、CopyOnWriteArrayList 和 synchronized对比

在 Java 中,Collections.synchronizedListCopyOnWriteArrayListsynchronized都可以用来解决多线程环境下的线程安全问题,但它们的性能和适用场景有所不同。以下是三者的区别和性能对比:

1.Collections.synchronizedList

Collections.synchronizedList是通过对ArrayList进行包装,使用同步锁(synchronized)来保证线程安全。

工作原理:
  • Collections.synchronizedList在每个方法(如addget等)上都加了同步锁,确保同一时间只有一个线程可以操作该集合。
优点:
  • 线程安全:适用于多线程环境。
  • 简单易用:直接通过Collections.synchronizedList包装即可。
缺点:
  • 性能较低:由于每次操作都需要获取锁,多个线程同时访问时会产生锁竞争,性能会下降。
  • 迭代器不安全:在使用迭代器遍历时,仍需要手动同步,否则可能抛出ConcurrentModificationException
示例代码:
List<String> list = Collections.synchronizedList(new ArrayList<>()); synchronized (list) { // 遍历时需要手动加锁 for (String item : list) { System.out.println(item); } }
适用场景:
  • 适合读写操作比例相对均衡的场景。
  • 适合对线程安全要求较高,但性能要求不高的场景。

2.CopyOnWriteArrayList

CopyOnWriteArrayList是一种线程安全的集合,它的实现方式是写时复制

工作原理:
  • 每次对集合进行写操作(如addremove等)时,会复制一份底层数组,修改完成后再将新的数组替换旧的数组。
  • 读操作不需要加锁,因为底层数组是只读的,读操作不会影响数据一致性。
优点:
  • 读操作性能高:读操作不需要加锁,多个线程可以同时读取数据。
  • 线程安全:写操作通过复制数组实现,避免了锁竞争。
  • 迭代器安全:迭代器是基于快照的,不会抛出ConcurrentModificationException
缺点:
  • 写操作性能较低:每次写操作都会复制整个数组,开销较大。
  • 内存占用高:频繁的写操作会导致大量的内存分配和垃圾回收。
示例代码:
List<String> list = new CopyOnWriteArrayList<>(); list.add("A"); list.add("B"); for (String item : list) { // 迭代时不需要加锁 System.out.println(item); }
适用场景:
  • 适合读多写少的场景,例如缓存、配置管理等。
  • 不适合频繁写操作的场景。

3.synchronized

synchronized是 Java 提供的关键字,用于显式地对代码块或方法加锁。

工作原理:
  • synchronized可以用来锁住代码块或方法,确保同一时间只有一个线程可以执行被锁住的代码。
优点:
  • 灵活性高:可以精确控制锁的范围。
  • 线程安全:通过锁机制保证线程安全。
缺点:
  • 性能较低:锁的粒度较大,容易导致线程阻塞和性能下降。
  • 代码复杂度高:需要手动管理锁的范围,容易出错。
示例代码:
List<String> list = new ArrayList<>(); synchronized (list) { list.add("A"); list.add("B"); }
适用场景:
  • 适合需要对代码块或方法进行精细化控制的场景。
  • 不适合需要频繁操作集合的场景,容易导致性能瓶颈。

性能对比

特性Collections.synchronizedListCopyOnWriteArrayListsynchronized
线程安全性
读操作性能中等(需要加锁)高(无锁)低(需要加锁)
写操作性能中等(需要加锁)低(写时复制,开销大)低(需要加锁)
迭代器安全性不安全(需手动加锁)安全(基于快照)不安全(需手动加锁)
适用场景读写均衡场景读多写少场景灵活控制锁的场景
内存占用高(写时复制会占用更多内存)

总结

  1. Collections.synchronizedList

    • 适合读写操作均衡的场景。
    • 性能一般,但实现简单。
  2. CopyOnWriteArrayList

    • 适合读多写少的场景。
    • 读操作性能高,但写操作性能较低,内存占用较高。
  3. synchronized

    • 适合需要精确控制锁的场景。
    • 性能较低,代码复杂度较高。

选择建议

  • 如果你的场景是读多写少,推荐使用CopyOnWriteArrayList
  • 如果你的场景是读写均衡,推荐使用Collections.synchronizedList
  • 如果需要对代码块进行精细化的线程安全控制,使用synchronized

测试代码:

public class ListTest { private static void synchronizedTest() throws InterruptedException { long start = System.currentTimeMillis(); /** * ArrayList 是非线程安全的,多个线程同时操作时可能导致索引越界。 * * Exception in thread "Thread-20" java.lang.ArrayIndexOutOfBoundsException: 10 * at java.util.ArrayList.add(ArrayList.java:465) */ List<String> list = new ArrayList<>(); int threadCount = 1000; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } // 626, 617 synchronized (list) { list.add("1"); } countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("list.size():" + list.size() + " 耗时:" + (System.currentTimeMillis() - start)); for (String content : list) { System.out.println("content:" + content); } } private static void synchronizedListTest() throws InterruptedException { long start = System.currentTimeMillis(); /** * ArrayList 是非线程安全的,多个线程同时操作时可能导致索引越界。 * * Exception in thread "Thread-20" java.lang.ArrayIndexOutOfBoundsException: 10 * at java.util.ArrayList.add(ArrayList.java:465) */ List<String> list = Collections.synchronizedList(new ArrayList<>());// 622 int threadCount = 1000; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } list.add("1"); countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("list.size():" + list.size() + " 耗时:" + (System.currentTimeMillis() - start)); for (String content : list) { System.out.println("content:" + content); } } private static void copyOnWriteArrayListTest() throws InterruptedException { long start = System.currentTimeMillis(); /** * ArrayList 是非线程安全的,多个线程同时操作时可能导致索引越界。 * * Exception in thread "Thread-20" java.lang.ArrayIndexOutOfBoundsException: 10 * at java.util.ArrayList.add(ArrayList.java:465) */ List<String> list = new CopyOnWriteArrayList<>();// 622 int threadCount = 1000; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } list.add("1"); countDownLatch.countDown(); }).start(); } countDownLatch.await(); System.out.println("list.size():" + list.size() + " 耗时:" + (System.currentTimeMillis() - start)); for (String content : list) { System.out.println("content:" + content); } } public static void main(String[] args) throws InterruptedException { synchronizedTest(); synchronizedListTest(); copyOnWriteArrayListTest(); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 6:16:40

同样是技术岗,35 岁后为何网络安全行业越老越吃香?

前几天我表弟小王来找我喝茶&#xff0c;聊着聊着突然问我&#xff1a;“老曹&#xff0c;你说我要不要转行做网络安全啊&#xff1f; 听说这行业挺赚钱的。 “我一听就笑了&#xff0c;这不正好最近我刚研究过这个行业吗&#xff1f; 我跟他说&#xff0c;别看现在各行各业…

作者头像 李华
网站建设 2026/5/25 13:27:14

[特殊字符] uni-app App 端实现文件上传功能(基于 xe-upload 插件)

在 uni-app 开发中&#xff0c;文件上传是一个常见且重要的功能。尤其是在 App 端&#xff0c;如何实现一个既美观又实用的文件上传与展示界面&#xff0c;是很多开发者关心的问题。本文将介绍如何通过 xe-upload 插件&#xff0c;结合自定义 UI&#xff0c;实现一个完整的文件…

作者头像 李华
网站建设 2026/5/26 4:27:47

二、Visual Studio 2026如何创建C语言项目

1.打开软件2.创建新项目3.下一步4.创建5.添加源文件&#xff08;1&#xff09;新建项&#xff08;2&#xff09;显示所有模板&#xff08;3&#xff09;添加C语言源文件后缀名为.c6.添加头文件&#xff08;1&#xff09;新建项&#xff08;2&#xff09;显示所有模板&#xff0…

作者头像 李华
网站建设 2026/5/25 15:19:00

Java二叉树:原理、实现与实战

深入浅出Java二叉树&#xff1a;原理、实现与实战 一、二叉树核心概念深度解析 1. 二叉树的定义与分类 二叉树是一种每个节点最多有2个子节点的树状结构&#xff0c;子节点分为左子节点&#xff08;lChild&#xff09;和右子节点&#xff08;rChild&#xff09;。根据节点分布规…

作者头像 李华
网站建设 2026/5/25 22:05:03

动态规划(四)算法设计与分析 国科大

0-1背包问题输入&#xff1a;给定物品集合 &#xff0c;每个物品 i 对应重量 和价值&#xff1b;同时给定背包的总重量限制 W。输出&#xff1a;选择物品的一个子集&#xff0c;满足 “子集总重量不超过 W” 的约束&#xff0c;同时最大化子集的总价值。这是一个二元决策问题&a…

作者头像 李华
网站建设 2026/5/25 23:31:28

为什么90%的团队搞不定云原生Agent部署?Docker批量方案深度拆解

第一章&#xff1a;云原生Agent部署的现状与挑战随着云原生技术的快速发展&#xff0c;Agent作为实现可观测性、自动化运维和安全监控的核心组件&#xff0c;被广泛部署于Kubernetes集群、边缘节点及混合云环境中。这些轻量级代理程序负责采集指标、日志和追踪数据&#xff0c;…

作者头像 李华