在Java高并发环境下,实现线程安全的List和Map有以下几种常见方案

一、线程安全List

1. 同步包装类
复制代码
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
  • 通过synchronized方法实现线程安全

  • 迭代时需要手动加锁

  • 适合低并发场景

2. CopyOnWriteArrayList(推荐)
复制代码
List<String> copyList = new CopyOnWriteArrayList<>();
  • 写时复制:修改时创建新数组,读操作无锁

  • 适合读多写少场景

  • 迭代器是快照,不会抛出ConcurrentModificationException

3. Vector(已过时)
复制代码
Vector<String> vector = new Vector<>();
  • JDK1.0遗留类,方法使用synchronized修饰

  • 性能较差,不推荐使用


二、线程安全Map

1. ConcurrentHashMap(推荐)
java 复制代码
Map<String, Object> concurrentMap = new ConcurrentHashMap<>();
  • JDK1.7:分段锁(Segment)

  • JDK1.8+:synchronized+ CAS + 红黑树

  • 高并发下性能优秀

  • 不允许null键/值

2. Collections.synchronizedMap
java 复制代码
Map<String, Object> syncMap = Collections.synchronizedMap(new HashMap<>());
  • 使用synchronized包装所有方法

  • 迭代时需要手动同步

3. ConcurrentSkipListMap
java 复制代码
Map<String, Object> skipMap = new ConcurrentSkipListMap<>();
  • 基于跳表实现的有序并发Map

  • 支持范围查询

4. Hashtable(已过时)
java 复制代码
Hashtable<String, Object> table = new Hashtable<>();
  • 与Vector类似,全表锁性能差

三、选择策略

场景 List推荐方案 Map推荐方案
读多写少 CopyOnWriteArrayList ConcurrentHashMap
写多读少 Collections.synchronizedList ConcurrentHashMap
需要有序 - ConcurrentSkipListMap
低并发 同步包装类 同步包装类

四、注意事项

  1. 复合操作问题

    java 复制代码
    // 即使使用ConcurrentHashMap,以下操作仍然非原子
    if (!map.containsKey(key)) {
        map.put(key, value); // 可能存在竞态条件
    }
    // 应使用
    map.putIfAbsent(key, value);
  2. 迭代器弱一致性

    • 并发集合的迭代器是弱一致

    • 不保证迭代时看到所有更新

  3. 性能考量

    • CopyOnWriteArrayList写操作昂贵(复制数组)

    • ConcurrentHashMap扩容时效率较高


五、JDK8+增强

java 复制代码
// ConcurrentHashMap的原子操作
concurrentMap.computeIfAbsent(key, k -> createValue(k));
concurrentMap.merge(key, value, (oldVal, newVal) -> mergeFunction(oldVal, newVal));

// 批量操作
concurrentMap.forEach(parallelismThreshold, action);
concurrentMap.search(parallelismThreshold, searchFunction);

最佳实践

  1. 优先使用java.util.concurrent包下的并发集合

  2. 明确业务场景的读写比例

  3. 使用原子方法避免复合操作竞态条件

  4. 考虑使用ThreadLocal减少竞争

  5. 高并发场景避免使用Collections.synchronizedXXX

高并发环境下线程安全的去重List实现方案

一、核心解决方案

1. 使用 CopyOnWriteArraySet

java 复制代码
Set<String> uniqueSet = new CopyOnWriteArraySet<>();
// 自动去重,线程安全
uniqueSet.add("item1");
uniqueSet.add("item1"); // 不会被添加

特点

  • 内部使用CopyOnWriteArrayList实现

  • 添加时会遍历检查是否已存在(O(n)复杂度)

  • 适合读多写少的场景

  • 保证元素唯一性

2. 使用 ConcurrentHashMap 模拟去重List

java 复制代码
// 使用ConcurrentHashMap模拟去重List
Map<String, Boolean> map = new ConcurrentHashMap<>();
List<String> list = Collections.synchronizedList(new ArrayList<>());

public boolean addIfAbsent(String item) {
    if (map.putIfAbsent(item, Boolean.TRUE) == null) {
        list.add(item);
        return true;
    }
    return false;
}
相关推荐
bbq粉刷匠8 小时前
HashMap 底层原理深度拆解(二):putVal 完整链路解析(懒加载 · 链表遍历 · 尾插法)
java·开发语言·哈希算法
Sam_Deep_Thinking8 小时前
一个靠谱的C端网关服务应该包含什么内容
java·程序员·系统架构
杨运交8 小时前
[055][调度模块]Spring动态任务调度框架的设计与实现
java·后端·spring
白狐_7989 小时前
408 数据结构算法题 01:线性表暴力求解保分指南
java·数据结构·算法
流云鹤9 小时前
03Java学习day(3)
java·学习
tqs_123459 小时前
Agent智能体+Skill插件引擎+Milvus混合检索 技术沉淀
java·python·milvus
数据知道9 小时前
容器安全入门:Docker 逃逸原理与 5 个经典 CVE
安全·网络安全·docker·容器
严同学正在努力9 小时前
从备份到恢复:我用 30 分钟恢复了误删的核心业务表
android·java·数据库·ai
JonLee202010 小时前
适配 Yii 3.0,php-casbin/yii-permission 3.0 正式发布
安全·php·rbac·yii·权限·casbin
QQ_216962909610 小时前
Spring Boot 养老院管理系统:从入住、护理到费用结算的全流程实现(源码可领)
java·spring boot·后端