Collections.synchronizedMap仅保证单个方法调用线程安全,复合操作(如containsKey+put)和迭代需手动加锁syncMap;适用于低并发读多写少场景,不适用于高频更新或需原子复合操作的高并发场景。

Collections.synchronizedMap 是一种轻量级线程安全包装方式,适合低并发、读多写少的场景,但不能替代真正的高并发集合。它不是“开箱即用”的万能方案,关键在于理解其同步边界和手动补位的位置。
单个操作自动同步,但仅限方法级别
put、get、remove、size 等每个 public 方法内部都加了 synchronized(mutex),因此单独调用是线程安全的。例如:
- ✅ 安全:syncMap.put("a", 1); —— 一次写入,锁住整个 map,原子完成
- ✅ 安全:Integer v = syncMap.get("a"); —— 一次读取,无竞态风险
- ❌ 危险:if (!syncMap.containsKey("a")) syncMap.put("a", 1); —— 两次独立同步调用,中间存在时间窗口
复合操作必须显式加锁
所有涉及“判断+执行”逻辑的操作,都需用 synchronized 块包裹,且锁对象必须是包装后的 syncMap(不是原始 map):
- 正确写法:
synchronized (syncMap) { if (!syncMap.containsKey(k)) syncMap.put(k, v); } - 错误写法:
synchronized (rawMap) { ... }或synchronized (new Object()) { ... }—— 锁不匹配,无效 - 典型场景包括:懒初始化值、计数器累加、条件覆盖写入等
迭代必须在同步块内完成
entrySet()、keySet()、values() 返回的视图本身不带同步,其迭代器是 fail-fast 的。外部线程修改会触发 ConcurrentModificationException:
立即学习“Java免费学习笔记(深入)”;
- 正确遍历:
synchronized (syncMap) { for (var e : syncMap.entrySet()) { ... } } - 错误遍历:
for (var e : syncMap.entrySet()) { ... }—— 即使只读,也可能因其他线程写入而崩溃 - 若需边遍历边修改,应改用 ConcurrentHashMap 并配合其弱一致性迭代器
适用与不适用的典型场景
选它,是因为简单;弃它,是因为瓶颈明显:
- ✅ 合适:应用启动时加载的静态配置表、国家码映射、HTTP 状态码说明等只读或极少更新的数据
- ✅ 合适:单元测试中快速构造共享状态容器,无需引入额外依赖
- ❌ 不合适:高频 put/remove(如实时统计、会话缓存)、大量并发遍历、要求非阻塞读的后台服务
- ❌ 不合适:需要 computeIfAbsent、merge、replace 等原子语义的业务逻辑


















