并发容器
ConcurrentHashMap
-
设计原理
- 分段锁(JDK 7) :将数据分成多个段(Segment),每个段独立加锁,不同段的操作可并行执行。
- CAS + synchronized(JDK 8+) :取消分段锁,改用
CAS
(无锁算法)和细粒度synchronized
锁桶(Node),进一步提升并发性能。
-
核心特性
- 线程安全:支持多线程并发读写,无需外部同步。
- 高吞吐量:锁的粒度更细,减少线程竞争。
- 弱一致性 :迭代器遍历时可能不反映最新修改(但不抛出
ConcurrentModificationException
)。
-
适用场景
- 高并发读写(如缓存、计数器)。
- 替代
Hashtable
或Collections.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 方法(生产者直接等待消费者取走元素) |
阻塞队列的工作原理
- 线程安全机制 :内部通过
ReentrantLock
和Condition
实现线程同步 - 锁分离 :插入和取出操作使用不同的锁(如
LinkedBlockingQueue
的putLock
和takeLock
),减少竞争。 - 条件变量 :队列空或满时,通过
Condition
的await()
和signal()
实现阻塞与唤醒。
阻塞队列的典型使用场景
- 生产者-消费者模式:适合任务量不可控但需快速响应的场景(如日志处理)。
- 线程池任务调度 :Java线程池(如
ThreadPoolExecutor
)默认使用LinkedBlockingQueue
存储待执行任务。 - 流量削峰与系统解耦:在高并发场景下,用队列缓冲瞬时流量,保护下游系统。
阻塞队列的选择与调优
-
队列容量
- 有界队列 (如
ArrayBlockingQueue
):避免内存溢出,但需合理设置容量,防止任务被拒绝。 - 无界队列 (如
LinkedBlockingQueue
):可能因任务堆积导致内存耗尽,需谨慎使用。
- 有界队列 (如
-
公平性
-
公平锁(
fairness=true
)减少线程饥饿,但降低吞吐量;非公平锁(默认)反之。iniArrayBlockingQueue<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 |
多线程相互等待至屏障点 | 可重用 | 分阶段任务、数据合并 |