当前位置: 首页 > news >正文

JUC 并发集合:高效处理多线程数据共享的利器

在 Java 并发编程中,数据共享和线程安全是核心问题。Java 集合框架中的普通集合(如 ArrayList、HashMap)并非线程安全,在多线程环境下使用可能导致数据不一致或异常。为此,JDK 的 java.util.concurrent(JUC)包提供了一系列线程安全的并发集合,专门用于解决多线程环境下的数据操作问题。本文将详细介绍 JUC 并发集合的分类、特性及典型使用场景。

一、JUC 并发集合概述

JUC 并发集合位于 java.util.concurrent 包下,它们通过巧妙的并发设计(如分段锁、CAS 操作、Copy-On-Write 等)实现了高效的线程安全,避免了使用 synchronized 带来的性能开销。与传统同步集合(如 Vector、Hashtable)相比,JUC 并发集合具有更高的吞吐量和更好的并发性能。

根据功能,JUC 并发集合可分为以下几类:

  • ConcurrentMap:并发映射
  • ConcurrentCollection:并发集合
  • 阻塞队列(BlockingQueue)
  • 其他同步工具类

二、常用 JUC 并发集合详解

1. ConcurrentHashMap

ConcurrentHashMap 是 HashMap 的线程安全版本,也是 JUC 中最常用的并发集合之一。它在 JDK 1.7 中采用分段锁(Segment)机制,JDK 1.8 及以上则使用 CAS+synchronized 实现,进一步提升了并发性能。

特性

  • 支持高并发的读操作,读操作无需加锁
  • 写操作只锁定当前操作的节点,不影响其他节点的读写
  • 提供原子性的 putIfAbsent、remove 等操作

使用示例

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapDemo {public static void main(String[] args) {Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();// 启动多个线程进行put操作for (int i = 0; i < 5; i++) {int threadNum = i;new Thread(() -> {for (int j = 0; j < 1000; j++) {String key = "key-" + j;// 原子操作:如果key不存在则put,返回旧值concurrentMap.putIfAbsent(key, threadNum);}}, "Thread-" + i).start();}// 等待所有线程完成try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("集合大小: " + concurrentMap.size());}
}

2. CopyOnWriteArrayList

CopyOnWriteArrayList 是 ArrayList 的线程安全变体,其核心思想是 "写时复制":当进行修改操作(add、set 等)时,会创建底层数组的副本,修改操作在副本上进行,完成后再将引用指向新数组。

特性

  • 读操作无锁,性能优异
  • 写操作需要复制数组,开销较大
  • 适合读多写少的场景

使用示例

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {List<String> list = new CopyOnWriteArrayList<>();// 添加元素list.add("元素1");list.add("元素2");list.add("元素3");// 迭代过程中可以安全修改集合Iterator<String> iterator = list.iterator();new Thread(() -> {list.add("新元素");System.out.println("添加新元素后集合: " + list);}).start();// 迭代器遍历的是原数组,不会反映新添加的元素System.out.println("迭代器遍历结果:");while (iterator.hasNext()) {System.out.println(iterator.next());}}
}

3. BlockingQueue 接口

BlockingQueue 是支持阻塞操作的队列,当队列满时,入队操作会阻塞;当队列空时,出队操作会阻塞。它是实现生产者 - 消费者模式的理想选择。

常用实现类:

  • ArrayBlockingQueue:基于数组的有界阻塞队列
  • LinkedBlockingQueue:基于链表的可选有界阻塞队列
  • SynchronousQueue:不存储元素的阻塞队列,每个 put 必须等待一个 take
  • PriorityBlockingQueue:支持优先级的无界阻塞队列
  • DelayQueue:支持延迟获取元素的无界阻塞队列

生产者 - 消费者模式示例

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class BlockingQueueDemo {// 创建容量为10的有界阻塞队列private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);public static void main(String[] args) {// 启动生产者线程new Thread(() -> {try {for (int i = 0; i < 20; i++) {queue.put(i); // 队列满时会阻塞System.out.println("生产者生产: " + i + ",队列大小: " + queue.size());Thread.sleep(100);}} catch (InterruptedException e) {e.printStackTrace();}}, "生产者").start();// 启动消费者线程new Thread(() -> {try {for (int i = 0; i < 20; i++) {Integer value = queue.take(); // 队列空时会阻塞System.out.println("消费者消费: " + value + ",队列大小: " + queue.size());Thread.sleep(300);}} catch (InterruptedException e) {e.printStackTrace();}}, "消费者").start();}
}

4. ConcurrentLinkedQueue

ConcurrentLinkedQueue 是基于链表的无界非阻塞队列,它使用 CAS 操作保证线程安全,适合高并发场景下的队列操作。

特性

  • 无界队列,理论上可以无限添加元素
  • 非阻塞算法实现,高并发性能好
  • 不允许 null 元素

使用示例

import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentLinkedQueueDemo {public static void main(String[] args) {ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();// 入队操作queue.offer("元素1");queue.offer("元素2");queue.offer("元素3");// 出队操作System.out.println(queue.poll()); // 元素1System.out.println(queue.poll()); // 元素2// 查看队头元素System.out.println(queue.peek()); // 元素3// 遍历队列System.out.println("队列元素:");queue.forEach(System.out::println);}
}

5. 其他实用并发集合

  • ConcurrentSkipListMap:支持排序的并发 Map,类似于 TreeMap 的线程安全版本
  • ConcurrentSkipListSet:支持排序的并发 Set,内部基于 ConcurrentSkipListMap 实现
  • CopyOnWriteArraySet:基于 CopyOnWriteArrayList 实现的 Set,适合读多写少场景

三、JUC 并发集合与传统同步集合的对比

特性JUC 并发集合传统同步集合(如 Vector、Hashtable)
线程安全实现采用 CAS、分段锁等机制基于 synchronized 方法
并发性能高,支持多线程同时读写低,同一时间只允许一个线程操作
迭代器特性弱一致性迭代器,不抛出 ConcurrentModificationException快速失败迭代器,可能抛出 ConcurrentModificationException
适用场景高并发读写场景低并发或单线程场景

四、选择合适的 JUC 并发集合

选择 JUC 并发集合时,可参考以下原则:

  1. 判断是集合还是映射:需要键值对结构选择 ConcurrentHashMap 或 ConcurrentSkipListMap;需要线性结构选择相应的集合类。

  2. 考虑读写比例

    • 读多写少:优先选择 CopyOnWriteArrayList、CopyOnWriteArraySet
    • 读写均衡或写操作较多:选择 ConcurrentLinkedQueue、ConcurrentHashMap 等
  3. 是否需要阻塞功能:实现生产者 - 消费者模式时,优先选择 BlockingQueue 的实现类。

  4. 是否需要排序:需要排序功能时选择 ConcurrentSkipListMap 或 ConcurrentSkipListSet。

  5. 是否有界:对队列大小有限制时选择 ArrayBlockingQueue;无限制时可选择 LinkedBlockingQueue 或 ConcurrentLinkedQueue。

五、总结

JUC 并发集合为多线程环境下的数据操作提供了高效、安全的解决方案。它们通过精心设计的并发控制机制,在保证线程安全的同时,最大限度地提高了并发性能。

掌握 JUC 并发集合的特性和适用场景,能够帮助我们在实际开发中选择合适的集合类型,编写高效、健壮的并发程序。在使用时,应根据具体的业务场景和性能需求,合理选择最适合的并发集合,以达到最佳的程序性能。

http://www.dtcms.com/a/363955.html

相关文章:

  • 开源的聚合支付系统源码/易支付系统 /三方支付系统
  • More Effective C++ 条款24:理解虚拟函数、多继承、虚继承和RTTI的成本
  • 第一次用pyQt6制作JSON小工具
  • =Windows下VSCode配置SSH密钥远程登录
  • C语言中奇技淫巧08-使用alloca/__builtin_alloca从栈上分配空间
  • 打工人日报#20250902
  • 自动化运维-ansible中的循环应用
  • 机器学习入门,支持向量机
  • etf期权亏几个点就爆仓了?
  • 37.Ansible循环+常用过滤器
  • docker-compose的使用
  • 让AI成为您的眼睛:星眸(StarGaze),为盲人朋友点亮前行之路
  • MySQL8.0 新特性随笔
  • 基于B_S结构的校园报修管理系统设计与实现(代码+数据库+LW)
  • 设置STS(Spring Tool Suite),在格式化代码时for循环中的冒号左右都加上一个空格
  • 移动端网页调试实战,Safari Web Inspector 深度使用与对比分析
  • 关于?问号占位符的分析(主要以PHP为例)
  • C# 中这几个主流的 ORM(对象关系映射器):Dapper、Entity Framework (EF) Core 和 EF 6
  • C#基础(⑥动态链接库DLL)
  • Python 中将 JSON 字符串转为对象的几种方法对比
  • (五)Python控制结构(循环结构)
  • 最快的 C 语言 JSON 库 - yyjson
  • 爬虫-----最全的爬虫库介绍(一篇文章让你成为爬虫大佬,爬你想爬)
  • 【鸿蒙心迹】从疑惑到热爱:我的鸿蒙开发启蒙
  • Java消息中间件(RocketMQ)
  • Linux 文本处理实战手册
  • 【专栏升级】大模型应用实战并收录RAG专题,Agent专题,LLM重构数据科学流程专题,端侧AI专题,累计63篇文章
  • Redis 哨兵 (基于 Docker)
  • YOLO 目标检测:YOLOv5网络结构、Focus、CSP、自适应Anchor、激活函数SiLU、SPPF、C3
  • 3.2-C++基础组件