跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何应用 Collections.synchronizedCollection 实战为基础集合变量强行织入线程安全同步锁

Collections.synchronizedCollection仅保证单个操作原子性,不保证复合操作线程安全;需手动同步检查-添加、迭代等场景,或改用ConcurrentHashMap等原生并发集合。 直接用
Collections.synchronizedCollection
包一层,**并不能让集合“自动线程安全”**——它只保证单个操作(如
add
、
remove
)原子,但复合操作(如“检查是否存在再添加”)仍需手动同步。实战中若忽略这点,极易出现竞态条件。 明确它的能力边界:只保单步原子,不保逻辑原子 该方法返回一个装饰器(Decorator),内部对每个公有方法加了
synchronized(this)
。例如:
Collection syncList = Collections.synchronizedCollection(new ArrayList<>()); syncList.add("a"); // ✅ 线程安全(底层已同步)
但下面这段代码依然不安全:
if (!syncList.contains("b")) { syncList.add("b"); // ❌ 两步之间可能被其他线程插入相同元素 }
因为
contains
和
add
是两次独立的同步调用,中间存在时间窗口。 必须显式同步的典型场景 以下操作不能依赖装饰器自身,需外层统一加锁: 检查-然后-操作(check-then-act) :如先查后删、先判空再取首元素 迭代遍历 :即使用了 synchronizedCollection,迭代器本身不是同步的,需手动同步整个遍历块 批量操作组合 :如“清空并重新填充”“按条件批量移除” 正确写法示例(以遍历为例):
Collection syncCol = Collections.synchronizedCollection(new ArrayList<>()); // ... synchronized (syncCol) { // 锁住装饰器对象本身 for (String s : syncCol) { System.out.println(s); } }
替代方案比强行加锁更实用 除非遗留系统强约束,否则优先考虑更现代、更安全的原生线程安全集合: 并发读多写少 → 用
CopyOnWriteArrayList
或
CopyOnWriteArraySet
(迭代安全,写操作开销大) 高并发增删查均衡 → 用
ConcurrentHashMap
(keySet/entrySet 可安全迭代),或其衍生集合如
ConcurrentLinkedQueue
需要阻塞等待 → 用
BlockingQueue
实现(如
LinkedBlockingQueue
) 它们在设计上就规避了“手动同步”的易错点,且性能通常优于全局锁。 真要用 synchronizedCollection?记住三个实操要点 锁对象必须是返回的装饰器实例本身 ,不要锁原始集合(如
new ArrayList<>()
),否则不同步 所有访问路径必须经过该装饰器引用 ,避免有人绕过它直操作底层集合 注意迭代器失效风险 :即使同步了遍历块,其他线程仍可能在遍历时修改集合(只是不会抛
ConcurrentModificationException
,但结果不可预期) 本质上,它是线程安全的“最低成本适配层”,适合快速包装已有集合接口,但绝不应成为并发逻辑的设计起点。

相关文章