在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;
}
相关推荐
ly76893 小时前
从 TF-IDF 到 BM25:Elasticsearch 相关性评分的字段长度归一化与 boost 调参边界
java·elasticsearch·query dsl·bm25·相关性评分
程序猿_极客3 小时前
【免费】分享一套优质的基于SpringBoot的旅游管理系统的设计与实现(带智能推荐算法功能),源码+文档+视频详解(讲解)
java·spring boot·课程设计·旅游管理系统·计算机专业项目
Wang's Blog3 小时前
Java 中间件之 RabbitMQ 快速入门: RabbitMQ 介绍与安装部署
java·中间件·java-rabbitmq
砚凝霜3 小时前
【软考信息安全】第十二章 网络安全审计概述与分类
网络·安全·web安全
大鹏的NLP博客3 小时前
Rust 的本质:用数学约束实现编译期内存安全
安全·rust
sibylyue3 小时前
SQLite 运行模式
java·jvm·sqlite
专业程序开发源3 小时前
django便利店外卖后台管理系统19650-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·django·课程设计
迪康妍妍3 小时前
终端安全实战:用迪康终端安全管理系统实现U盘四分档管控与全量审计
android·运维·网络·安全·电脑
JAVA面经实录9173 小时前
Java高级后端 · 架构组件开发 + CodeReview(本岗位差异化核心)
java·架构·代码复审
toooooop83 小时前
宝塔Python项目管理器:环境变量踩坑记录
java·jvm·python