synchronized 保护集合需统一以同一集合实例为锁,确保所有读写操作串行执行;封装为同步方法或用 Collections.synchronizedXxx() 可简化控制,但迭代仍需手动同步;高并发场景优先选 CopyOnWriteArrayList、ConcurrentHashMap 等专用线程安全集合。

用 synchronized 保护集合的并发修改安全,核心是确保对集合的读写操作都在同一个锁对象下串行执行。Java 原生集合(如 ArrayList、HashMap)本身不支持线程安全,直接在多线程中增删改查会触发 ConcurrentModificationException 或数据不一致,必须加锁协调。
用 synchronized 块锁定集合对象本身
最直接的方式是将集合对象作为锁,在所有访问该集合的地方统一使用 synchronized(collection) 包裹操作:
- 适用于自定义控制范围,比如只锁读或只锁写,或组合多个操作为原子动作(如“检查后插入”)
- 注意:必须保证所有线程都用同一个集合实例作锁,不能每次 new 一个新集合,也不能误锁别的对象
- 示例:不要写
synchronized(new ArrayList()),而要先声明共享变量:private final List<String> list = new ArrayList<>();
再在方法中:synchronized (list) { list.add("item"); }
用 synchronized 方法封装集合操作
把集合操作封装在 synchronized 实例方法中,隐式以 this 为锁(静态方法则锁类对象),适合集合属于某个线程安全的管理类:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 代码更简洁,调用方无需关心锁细节
- 要求集合是类的私有成员,且所有访问都走这些方法(不能暴露原始引用)
- 示例:
public synchronized void addItem(String s) { data.add(s); }public synchronized String getFirst() { return data.isEmpty() ? null : data.get(0); } - ⚠️ 若集合被返回给外部(如
public List<T> getData() { return data; }),外部可绕过同步直接操作,导致失效
避免常见陷阱:迭代时加锁必须覆盖整个遍历过程
对集合迭代(如 for-each、iterator)时,仅在开头加锁不够——因为 iterator.next() 内部仍会访问集合结构,若其他线程中途修改,仍会抛异常:
立即学习“Java免费学习笔记(深入)”;
- 正确做法:整个 for 循环体必须在 synchronized 块内
- 错误写法:
synchronized(list) {} // 只锁了空块for (String s : list) { ... } // 这里没锁! - 正确写法:
synchronized(list) {<br> for (String s : list) { /* 安全 */ }<br>} - 替代方案:考虑用
Collections.synchronizedList配合手动同步迭代(它只保证单个方法原子性,迭代仍需额外同步)
对比更优的现代替代方案
synchronized 能用,但不是唯一或总是最好的选择。根据场景可考虑:
-
CopyOnWriteArrayList:适合读多写少、迭代频繁的场景(写操作复制整个数组,迭代不加锁) -
ConcurrentHashMap:高并发下的键值存储首选,分段锁 + CAS,比synchronized HashMap吞吐量高得多 -
BlockingQueue(如LinkedBlockingQueue):生产者-消费者模型中天然线程安全,带阻塞语义 - 若只是临时需要线程安全集合,可用
Collections.synchronizedXxx(),但务必注意其迭代限制

















