Java并发工具包的精髓——从锁到队列再到原子变量

简介: java.util.concurrent包是Java并发编程的宝藏,它提供了一套经过精心设计的高性能并发工具,涵盖了锁、队列、同步器、原子变量、线程池等各个方面。

java.util.concurrent包是Java并发编程的宝藏,它提供了一套经过精心设计的高性能并发工具,涵盖了锁、队列、同步器、原子变量、线程池等各个方面。这个包的诞生极大地降低了并发编程的难度,使开发者无需重复造轮子,就能编写出正确且高效的并发程序。
参考:https://amwtm.cn/category/entrance.html

锁框架是java.util.concurrent.locks包的核心。与synchronized相比,Lock接口提供了更灵活的锁操作:tryLock(非阻塞获取锁)、lockInterruptibly(可中断的获取)、以及tryLock(long time, TimeUnit unit)(超时获取)。ReentrantLock是Lock的主要实现,它支持可重入(同一个线程可以多次获取锁)和公平性选择(公平锁按FIFO顺序分配,非公平锁允许插队)。在大多数场景下,非公平锁的吞吐量高于公平锁。

读写锁ReentrantReadWriteLock允许多个读线程同时访问,但写线程独占访问。这种锁在读多写少的场景下可以显著提高并发性。ReadWriteLock的升级/降级需要谨慎处理——从读锁升级到写锁可能导致死锁。StampedLock(Java 8)是读写锁的改进版本,支持乐观读锁(无阻塞的读取),进一步提高了并发性。StampedLock不是可重入的,使用起来更复杂,但性能更好。

条件变量Condition与锁配合使用,替代传统的wait/notify。每个Condition绑定一个Lock,提供了更精细的等待/通知机制。与wait/notify不同,Condition支持多组等待队列、可中断等待、以及超时等待。典型的用法是:lock.lock(); try { while (condition) { cond.await(); } } finally { lock.unlock(); }。

同步器框架是AbstractQueuedSynchronizer(AQS),它是java.util.concurrent包中许多工具的基础,包括ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等。AQS使用FIFO队列管理等待线程,通过state字段表示同步状态。子类只需要实现tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared等方法,AQS负责线程的排队、阻塞和唤醒。
参考:https://amwtm.cn/category/balcony.html

Semaphore(信号量)用于控制同时访问某个资源的线程数量。Semaphore维护一组许可,线程在访问资源前必须获取许可,使用后释放许可。Semaphore可以用于限流(控制并发请求数)、资源池(如数据库连接池)以及实现互斥锁(许可数为1)。Semaphore支持公平和非公平模式。

CountDownLatch是一个倒计数器,允许一个或多个线程等待其他线程完成操作。计数器初始化为N,每个完成操作的线程调用countDown()使计数器减1,等待的线程调用await()阻塞直到计数器归零。CountDownLatch不可重用,一次性的特性使其适用于启动门(等待所有准备工作完成)和结束门(等待所有任务完成)。

CyclicBarrier类似于CountDownLatch,但可重用。它允许多个线程相互等待,直到所有线程都到达屏障点。CyclicBarrier可以传入一个屏障动作(Runnable),当所有线程到达时执行。CyclicBarrier适用于分阶段并行计算——如MapReduce的shuffle阶段。与CountDownLatch不同,CyclicBarrier的计数器可以重置并重用。

Exchanger是一个用于两个线程交换数据的同步点。线程A调用exchange(dataA)阻塞,直到线程B也调用exchange(dataB),然后两个线程交换数据并继续。Exchanger在生产者-消费者模式中很有用,例如两个线程交换缓冲区。

Phaser是Java 7引入的灵活同步器,可以看作是CyclicBarrier和CountDownLatch的升级版。Phaser支持动态注册/注销参与方、多阶段同步、以及分级(父子Phaser)。Phaser的API比前两者复杂,适用于分阶段的任务,如并行模拟或迭代算法。
参考:https://amwtm.cn/category/bathroom.html

并发集合是java.util.concurrent包的重要组成部分。ConcurrentHashMap是线程安全的哈希表,使用分段锁(Java 7)或CAS + synchronized(Java 8+)实现高并发。ConcurrentHashMap的迭代器是弱一致性的,不抛出ConcurrentModificationException。ConcurrentSkipListMap和ConcurrentSkipListSet提供了基于跳表的并发有序映射和集合。CopyOnWriteArrayList和CopyOnWriteArraySet适用于读多写少的场景,写操作复制整个底层数组,成本高昂。

阻塞队列是生产者-消费者模式的核心。ArrayBlockingQueue是有界队列,基于循环数组实现;LinkedBlockingQueue是可选的边界队列,基于链表实现;PriorityBlockingQueue是无界优先级队列;DelayQueue是延迟队列,元素只有在其延迟过期后才能被取出;SynchronousQueue是无缓冲队列,每个插入操作必须等待对应的移除操作。LinkedTransferQueue(Java 7)是LinkedBlockingQueue的增强版,增加了transfer和tryTransfer方法。

原子变量java.util.concurrent.atomic包提供了无锁的线程安全变量。AtomicInteger、AtomicLong、AtomicBoolean和AtomicReference使用CAS(Compare-And-Swap)操作实现原子更新。AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray扩展到了数组。LongAdder和DoubleAdder(Java 8)在高竞争场景下优于AtomicLong,因为它们使用分段累加(类似于ConcurrentHashMap的思路)。LongAccumulator和DoubleAccumulator提供了更灵活的累加函数。

CAS操作是原子变量的底层基础。CAS接受三个参数:内存地址、期望值、新值。如果内存地址的值等于期望值,则更新为新值;否则不做任何操作。CAS返回是否成功。CAS是无锁编程的核心,避免了上下文切换和锁竞争的开销。但CAS也有问题:ABA问题(值从A改为B又改回A,CAS误认为没有变化),通过版本号或AtomicStampedReference可以解决;自旋CAS如果长期不成功,会浪费CPU时间。

线程池ThreadPoolExecutor是Java并发工具包中使用最广泛的组件之一。核心参数包括:核心线程数、最大线程数、空闲线程存活时间、阻塞队列、以及拒绝策略。理解线程池的工作机制非常重要:当线程数小于核心线程数时,创建新线程处理任务;当线程数达到核心线程数时,任务进入队列;当队列满时,创建新线程直到最大线程数;当队列满且线程数达到最大时,执行拒绝策略。

预定义线程池Executors提供了几种常用配置:newFixedThreadPool(固定大小线程池,使用无界队列)、newCachedThreadPool(缓存线程池,核心线程数为0,最大线程数无界,使用同步队列)、newSingleThreadExecutor(单线程池)、newScheduledThreadPool(支持定时和延迟执行)。但阿里巴巴Java开发手册禁止使用Executors创建线程池,因为无界队列可能导致OOM,建议直接使用ThreadPoolExecutor。

ForkJoinPool是Java 7引入的专门用于分治任务的线程池。ForkJoinPool使用工作窃取算法——空闲线程可以从其他线程的队列尾部窃取任务,保持CPU忙碌。RecursiveTask(有返回值)和RecursiveAction(无返回值)是分治任务的基类。ForkJoinPool是并行流(Java 8)的底层实现。CompletableFuture(Java 8)使用ForkJoinPool.commonPool()作为默认的异步执行器。

CompletableFuture是Java 8中最强大的异步编程工具。它支持回调链、组合多个异步任务、异常处理、以及超时控制。CompletableFuture实现了Future和CompletionStage接口,提供了thenApply、thenAccept、thenCompose、thenCombine等组合方法。CompletableFuture的异步编排能力使其成为Java中异步编程的事实标准,与反应式编程(RxJava、Project Reactor)形成竞争。

java.util.concurrent包的演进反映了Java对并发编程的理解不断深化。从最初的synchronized和volatile,到JUC包的锁和原子变量,再到ForkJoinPool和CompletableFuture,Java提供了从低级到高级的全方位并发工具。对于大多数应用,使用高级工具(如CompletableFuture和并发集合)就足够了,只有在性能关键的场景才需要深入到底层工具。理解这些工具的设计原理和使用场景,是成为Java并发专家的必经之路。
参考:https://amwtm.cn

目录
相关文章
|
1月前
|
存储 Java 应用服务中间件
Java类加载器的迷宫——双亲委派模型的来龙去脉
Java类加载器是JVM中最神秘也最强大的组件之一。它负责将类的字节码加载到JVM中,转换为java.lang.Class实例。
118 2
|
1月前
|
IDE 安全 Java
Java的模块化长征——从OSGi到JPMS的十年恩怨
Java模块化是一场持续了十多年的运动,经历了从第三方OSGi标准到官方JPMS(Java平台模块系统)的漫长演进。
122 1
|
1月前
|
缓存 安全 Java
Java中的反射与动态代理——元编程的艺术与代价
反射是Java语言的一项强大特性,它允许程序在运行时检查和修改自身的行为。
103 1
|
1月前
|
监控 算法 Java
Java垃圾回收的五十年——从标记清除到ZGC的演进之路
垃圾回收是Java平台的标志性特性,也是无数开发者选择Java而非C++的重要原因。
158 1
|
1月前
|
设计模式 Java Linux
Java I/O的演进——从BIO到NIO再到AIO的异步革命
Java I/O模型经历了从阻塞I/O(BIO)到非阻塞I/O(NIO)再到异步I/O(AIO)的演进,每一次演进都是对高并发、高吞吐量需求的回应。
133 1
|
1月前
|
Java 应用服务中间件 数据库连接
Java虚拟线程——Project Loom带来的并发革命
虚拟线程(Virtual Threads)是Java 21中最引人注目的特性,也是Project Loom的核心成果。
189 1
|
1月前
|
存储 缓存 Java
Java字节码的黑盒——从.class文件到JIT编译的优化之道
Java字节码是Java程序运行的中间表示形式,介于源代码和机器码之间。字节码的设计目标是平台无关性和紧凑性,使得"Write Once, Run Anywhere"成为可能。
136 1
|
1月前
|
缓存 监控 Java
Java性能调优实战——从JVM参数到火焰图
Java性能调优是一门需要经验和技巧的艺术。它涉及JVM参数调优、垃圾回收优化、线程分析、内存分析、以及代码级优化等多个层面。
158 0
|
1月前
|
存储 缓存 安全
Java内存模型的迷雾——从 happens-before 到 volatile 的可见性保证
Java内存模型是Java并发编程的基石,也是最容易被误解的领域之一。
105 0
|
存储 Java
AQS(AbstractQueuedSynchronizer,队列同步器)源码解读
AQS(AbstractQueuedSynchronizer,队列同步器)源码解读

热门文章

最新文章