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

目录
相关文章
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
32698 79
如何保证分布式文件系统的数据一致性
|
前端开发 容器
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局(上)
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局
17753 20
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36684 19
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24758 14
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36662 15
重生之---我测阿里云U1实例(通用算力型)
|
SQL 存储 弹性计算
Redis性能高30%,阿里云倚天ECS性能摸底和迁移实践
Redis在倚天ECS环境下与同规格的基于 x86 的 ECS 实例相比,Redis 部署在基于 Yitian 710 的 ECS 上可获得高达 30% 的吞吐量优势。成本方面基于倚天710的G8y实例售价比G7实例低23%,总性价比提高50%;按照相同算法,相对G8a,性价比为1.4倍左右。
|
存储 算法 Java
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的限流器RateLimiter功能服务
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
29838 52

热门文章

最新文章

下一篇
开通oss服务