在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;
}
相关推荐
武天旭5 小时前
AI安全--AI大模型的对抗样本攻击与防御解析
人工智能·深度学习·安全·机器学习
这个昵称不容易6 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长6 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
文丑颜不良啊8 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼8 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
文丑颜不良啊10 小时前
LeetCode2161~2180题目解析
java·leetcode
文丑颜不良啊11 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发12 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
天天被压力12 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力12 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python