Java并发并发编程实战-并发容器和同步工具类

并发容器

ConcurrentHashMap

  • 设计原理

    • 分段锁(JDK 7) :将数据分成多个段(Segment),每个段独立加锁,不同段的操作可并行执行。
    • CAS + synchronized(JDK 8+) :取消分段锁,改用CAS(无锁算法)和细粒度synchronized锁桶(Node),进一步提升并发性能。
  • 核心特性

    • 线程安全:支持多线程并发读写,无需外部同步。
    • 高吞吐量:锁的粒度更细,减少线程竞争。
    • 弱一致性 :迭代器遍历时可能不反映最新修改(但不抛出ConcurrentModificationException)。
  • 适用场景

    • 高并发读写(如缓存、计数器)。
    • 替代HashtableCollections.synchronizedMap
  • 对比传统同步容器

    特性 ConcurrentHashMap Hashtable/synchronizedMap
    锁粒度 细粒度(桶级别) 粗粒度(整个表)
    并发性能
    迭代器一致性 弱一致性 强一致性(可能抛出异常)

CopyOnWriteArrayList

  • 设计原理

    • 写时复制(Copy-On-Write) :每次修改(增、删、改)时,复制底层数组,在新副本上操作,完成后替换原数组。
    • 读操作无锁:直接访问原数组,无需同步。
    • 写操作加锁:保证同一时刻只有一个线程修改。
  • 核心特性

    • 线程安全:读操作完全无锁,写操作通过复制保证安全。
    • 数据一致性:迭代器遍历的是创建时的数组快照,不反映后续修改。
    • 内存开销:频繁修改会导致大量数组复制,占用内存。
  • 适用场景

    • 读多写极少(如监听器列表、配置白名单)。
    • 替代Collections.synchronizedList,在读远多于写时性能更优。
  • 对比传统同步容器

    特性 CopyOnWriteArrayList synchronizedList
    读性能 无锁,极高 需加锁,较低
    写性能 复制数组,较低 直接修改,较高
    内存占用 高(频繁写时)
    数据一致性 快照一致性 强一致性

并发容器适用场景

  • 高并发读写ConcurrentHashMap
  • 读多写极少CopyOnWriteArrayList
  • 写多读少 → 考虑ConcurrentLinkedQueue或阻塞队列(如LinkedBlockingQueue
  • 强一致性需求 → 慎用CopyOnWriteArrayList,优先使用锁同步的容器。

阻塞队列

Java阻塞队列

实现类 数据结构 特性
ArrayBlockingQueue 数组 有界队列,固定容量,公平性可选(基于ReentrantLock
LinkedBlockingQueue 链表 可选有界或无界(默认Integer.MAX_VALUE),吞吐量通常更高
PriorityBlockingQueue 堆(优先级队列) 无界队列,元素按优先级排序(需实现Comparable或提供Comparator
SynchronousQueue 无存储 直接传递队列,插入操作必须等待取出操作(无缓冲,适合线程间直接传递任务)
DelayQueue 堆(延迟队列) 无界队列,元素需实现Delayed接口,按到期时间排序(用于定时任务调度)
LinkedTransferQueue 链表 混合阻塞队列,支持transfer方法(生产者直接等待消费者取走元素)

阻塞队列的工作原理

  • 线程安全机制 :内部通过ReentrantLockCondition实现线程同步
  • 锁分离 :插入和取出操作使用不同的锁(如LinkedBlockingQueueputLocktakeLock),减少竞争。
  • 条件变量 :队列空或满时,通过Conditionawait()signal()实现阻塞与唤醒。

阻塞队列的典型使用场景

  • 生产者-消费者模式:适合任务量不可控但需快速响应的场景(如日志处理)。
  • 线程池任务调度 :Java线程池(如ThreadPoolExecutor)默认使用LinkedBlockingQueue存储待执行任务。
  • 流量削峰与系统解耦:在高并发场景下,用队列缓冲瞬时流量,保护下游系统。

阻塞队列的选择与调优

  • 队列容量

    • 有界队列 (如ArrayBlockingQueue):避免内存溢出,但需合理设置容量,防止任务被拒绝。
    • 无界队列 (如LinkedBlockingQueue):可能因任务堆积导致内存耗尽,需谨慎使用。
  • 公平性

    • 公平锁(fairness=true)减少线程饥饿,但降低吞吐量;非公平锁(默认)反之。

      ini 复制代码
      ArrayBlockingQueue<Integer> fairQueue = new ArrayBlockingQueue<>(100, true);
  • 特殊需求

    • 优先级调度PriorityBlockingQueue
    • 延迟任务DelayQueue
    • 直接传递任务SynchronousQueue

阻塞队列的常见问题

  • 死锁风险

    • 场景:多个线程互相等待对方释放资源。
    • 解决 :使用超时方法(如poll(timeout)),避免无限阻塞。
  • 资源耗尽

    • 场景:无界队列导致内存溢出。
    • 解决 :改用有界队列,配合拒绝策略(如线程池的RejectedExecutionHandler)。
  • 性能瓶颈

    • 场景:队列成为系统吞吐量的瓶颈。
    • 解决 :优化队列类型(如LinkedTransferQueue提升吞吐量),或增加消费者线程。

同步工具类

CountDownLatch

  • 核心功能

    • 一次性屏障:允许一个或多个线程等待其他线程完成操作后再继续执行。
    • 计数器递减 :初始化时设定计数值(count),线程调用countDown()减少计数,计数归零时唤醒等待线程。
  • 主要方法

    • CountDownLatch(int count) :构造函数,指定初始计数。
    • await() :阻塞当前线程,直到计数归零。
    • countDown() :减少计数值,计数为0时唤醒所有等待线程。
  • 使用场景:主线程等待所有子任务完成;多线程等待同一事件触发(如模拟并发测试)

Semaphore

  • 核心功能

    • 控制资源访问的并发数 :通过许可证(permits)限制同时访问某资源的线程数量。
    • 可公平/非公平模式:默认非公平,按请求顺序或竞争获取许可证。
  • 主要方法

    • Semaphore(int permits) :构造函数,指定许可证数量。
    • acquire() :获取许可证(若无可用则阻塞)。
    • release() :释放许可证。
    • tryAcquire() :非阻塞尝试获取许可证。
  • 使用场景:限流(如数据库连接池);实现互斥锁(许可证为1的信号量)

CyclicBarrier

  • 核心功能

    • 可重置的屏障:让一组线程相互等待,直到所有线程到达屏障点后同时继续执行。
    • 支持回调 :所有线程到达屏障后,可触发一个Runnable任务。
  • 主要方法

    • CyclicBarrier(int parties) :构造函数,指定参与的线程数。
    • CyclicBarrier(int parties, Runnable barrierAction) :指定到达屏障后的回调任务。
    • await() :线程到达屏障点并等待其他线程。
  • 使用场景:分阶段并行计算;多线程数据合并(如分布式计算)

同步工具对比与选型

工具 核心机制 重用性 典型场景
CountDownLatch 等待其他线程完成 一次性 主线程等待子任务、并发触发
Semaphore 控制资源访问并发数 可重用 限流、资源池(如数据库连接)
CyclicBarrier 多线程相互等待至屏障点 可重用 分阶段任务、数据合并
相关推荐
你们补药再卷啦1 小时前
springboot 项目 jmeter简单测试流程
java·spring boot·后端
网安密谈1 小时前
SM算法核心技术解析与工程实践指南
后端
bobz9651 小时前
Keepalived 检查和通知脚本
后端
AKAMAI1 小时前
教程:在Linode平台上用TrueNAS搭建大规模存储系统
后端·云原生·云计算
盘盘宝藏1 小时前
idea搭建Python环境
后端·intellij idea
喵手1 小时前
Spring Boot 项目基于责任链模式实现复杂接口的解耦和动态编排!
spring boot·后端·责任链模式
大鹏dapeng1 小时前
使用gone v2 的 Provider 机制升级改造 goner/xorm 的过程记录
后端·设计模式·go
雷渊1 小时前
介绍一下RocketMQ的几种集群模式
java·后端·面试
讳疾忌医_note1 小时前
别再错用 C++ 线程池!正确姿势与常见误区大揭秘
后端
快乐源泉1 小时前
【设计模式】参数校验逻辑复杂,代码长?用责任链
后端·设计模式·go