让集合线程安全的几种靠谱方法

原文来自于:https://zha-ge.cn/java/46

让集合线程安全的几种靠谱方法

在 Java 开发中,线程安全问题总是让人头疼。回想自己刚入行时的经历,也曾因为忽视线程安全而吃过不少苦头。今天就来分享一些让集合线程安全的实践经验,希望能帮助大家少走弯路。


初入多线程的尝试

最初接触多线程时,我曾天真地认为,只要数据能被多个线程共享,就能提升效率。于是,我写了类似下面的代码:

java 复制代码
List<String> list = new ArrayList<>();
// 多线程 add
executor.submit(() -> list.add("hello"));

然而,运行后却遇到了各种棘手的问题:空指针异常、并发修改异常,甚至数据丢失。这些"意外惊喜"让我深刻认识到,线程安全问题不容忽视。


踩坑的教训

在解决线程安全问题的过程中,我也走过不少弯路:

  • 误以为 Vector 就是线程安全的,结果发现线程竞争严重,性能低下。
  • 使用 Collections.synchronizedList 包装 ArrayList,虽然解决了部分问题,但遍历操作仍然不安全。
  • 尝试 CopyOnWriteArrayList,却发现写操作时性能急剧下降。

下面是一个典型的遍历问题示例:

java 复制代码
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
for (String s : syncList) {
    // 多线程环境下,可能出现 ConcurrentModificationException
}

问题在于,synchronizedList 只对集合的增删改操作进行了同步,但遍历操作仍然需要额外的同步处理。


解决方案总结

经过一番探索和实践,我总结了几种常见的线程安全解决方案:

方案 优缺点分析 适用场景
Vector 同步机制简单,但性能较差 需要兼容旧代码时使用
Collections.synchronizedXXX 方法级别的同步,需手动处理遍历 小规模场景下的快速实现
CopyOnWriteArrayList 读多写少场景表现优异,写操作较慢 读多写少的业务场景
ConcurrentHashMap 性能优秀,支持高并发操作 大多数业务场景推荐使用

对于 Collections.synchronizedList 的遍历操作,正确的做法是手动加锁:

java 复制代码
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
synchronized (syncList) {
    for (String s : syncList) {
        // 安全遍历
    }
}

此外,CopyOnWriteArrayList 在写操作较少的场景下表现尤为出色:

java 复制代码
CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();
safeList.add("A");
safeList.forEach(item -> System.out.println(item));

实践中的几点建议

通过这些年的实践,我总结出以下几点经验:

  1. 别低估并发问题的复杂性,简单的同步机制往往无法解决问题。
  2. 选择合适的工具,根据业务场景选择最合适的集合类。
  3. 深入理解集合类的内部实现,这有助于更好地选择和使用它们。
  4. 优先使用 Java 并发包中的类,它们经过了严格的测试和优化。

总之,线程安全问题需要我们在设计阶段就给予足够的重视。选择合适的集合类,并正确使用同步机制,是保证代码健壮性的关键。希望这些经验能帮助大家在实际开发中少走弯路,写出更高效、更安全的代码。

相关推荐
步行cgn几秒前
为什么 Spring 建议使用构造器注入?
java·后端·spring
Nefu_lyh1 分钟前
【工作杂谈_20261012】数据平台与工程治理: Spark OOM 排查:Java Heap、Executor 与代码内存模型
java·大数据·spark
小蒜学长2 分钟前
基于Vue.js + Spring Boot的酒店客房预订系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·酒店客房预订系统
真恋寄语枫秋8 分钟前
【Java零基础入门28】Java包机制详解:package包定义、import导入、权限访问、项目分层管理
java
豆角焖肉8 分钟前
SpringBoot + Redis 缓存实战
java·spring boot·redis·后端·缓存·菜单缓存
西柚不烦恼9 分钟前
jenkins版本和java版本的依赖
java·开发语言·jenkins
牧瀬クリスだ12 分钟前
Ajax入门:从原生XHR到axios实战
java·spring boot·ajax·okhttp
豆角焖肉27 分钟前
SpringBoot+JWT 实现前后端分离登录鉴权
java·开发语言·spring boot·jwt·前后端分离·登录认证
Wang's Blog35 分钟前
Java 项目部署之 Docker工具快速入门: 自定义镜像的分层结构与写时复制
java·开发语言·docker
别动我齐刘海10 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人