返回 化神・技术源流秘录

JUC前世今生

博主
大约 24 分钟

从“大师杰作”到“并发基石”:JUC(java.util.concurrent)发展历程与核心知识点详解(超详细·最终补全版)

引言

在Java诞生的早期,并发编程主要依赖synchronized关键字、wait()/notify()机制以及Thread类。这些原语虽然能够解决基础的线程同步问题,但面对复杂的并发场景时显得力不从心:锁的粒度难以控制、缺乏灵活的锁获取机制、线程间协作方式单一、高性能并发容器缺失……开发者往往需要自己封装复杂的同步逻辑,代码易错且难以维护。

这种局面的终结,始于2004年JDK 5的一个重量级新成员——java.util.concurrent(简称JUC)。这个由并发领域泰斗Doug Lea主导设计的包,为Java带来了工业级的高性能并发工具集,彻底改变了Java并发编程的面貌。

此后近二十年,JUC伴随着Java语言的演进,不断优化、扩充,从最初的核心组件逐步发展成为一个涵盖原子操作、锁机制、线程池、并发容器、同步工具、Fork/Join框架等全方位能力的并发编程基石。今天,我们顺着时间线,用最详尽的笔触,还原JUC从诞生到2026年的完整发展历程。

第一章:前JUC时代——并发编程的混沌期(1996-2004)

1.1 早期Java并发原语

在JDK 5之前,Java开发者进行并发编程主要依赖以下几个原语:

并发原语引入版本功能与局限
synchronized关键字JDK 1.0内置锁,使用简单,但锁的获取释放不灵活,无法中断,缺乏超时机制
wait()/notify()/notifyAll()JDK 1.0线程间协作机制,但必须配合synchronized使用,唤醒是随机的
ThreadJDK 1.0基础线程操作,但线程创建销毁开销大,缺乏线程池管理
ThreadLocalJDK 1.2线程局部变量,用于避免共享
volatile关键字JDK 1.0保证可见性,但不保证原子性

这些原语存在几个核心痛点:

  • 锁机制不够灵活:无法尝试获取锁、无法设置超时、无法响应中断
  • 线程管理成本高:每次需要线程都new Thread(),缺乏复用机制
  • 并发容器缺失VectorHashtable虽然是线程安全的,但性能低下;而ArrayListHashMap等又不安全
  • 复杂同步场景难以实现:信号量、栅栏、读写锁等都需要开发者自己实现

1.2 Doug Lea的util.concurrent

在官方JUC包诞生之前,并发领域的泰斗Doug Lea已经在1990年代末开发了一个广为流传的并发工具库——util.concurrent。这个包包含了线程池、锁、同步器、并发容器等实现,被许多早期的高并发应用所采用。

Doug Lea后来成为JSR 166(Java并发工具包规范)的规范领导者,他将util.concurrent的经验和设计带入了Java官方标准库。JDK 5中的JUC包,正是以Doug Lea的工作为基础,经过标准化和优化后诞生的。

第二章:JUC的诞生——JDK 5(2004年)【里程碑】

2004年9月30日,随着JDK 5的发布,java.util.concurrent包正式成为Java标准库的一部分。这是Java并发编程史上最重要的里程碑,由Doug Lea领导实现,为Java带来了工业级的高并发工具集。

2.1 诞生的背景与意义

JUC包的设计目标非常明确:

  1. 弥补synchronized的不足:提供更灵活的锁机制
  2. 提供高性能并发容器:替代线程不安全的集合类
  3. 实现线程池化:降低线程创建销毁开销
  4. 提供标准同步工具:信号量、栅栏、倒计时锁等
  5. 无锁并发支持:基于CAS的原子操作

2.2 JSR-166与Java社区进程

JUC包的诞生是Java社区进程(JCP)的重要成果。JSR-166(Java Specification Request 166)是由Doug Lea提出的规范提案,旨在增强Java在多线程编程方面的能力。

JSR-166提案不仅包含了对Java内存模型的改进,还包含了一系列新的并发工具和类,如Lock接口、Condition接口、Semaphore、CountDownLatch等。这一提案的通过和执行,标志着Java并发编程进入了一个全新的时代。

2.3 JDK 5首次引入的核心组件

组件分类核心类/接口作用
锁机制Lock接口、ReentrantLockReadWriteLock提供比synchronized更灵活的锁操作
原子类AtomicIntegerAtomicLongAtomicReference基于CAS的无锁原子操作
线程池Executor接口、ThreadPoolExecutorExecutors线程生命周期管理、任务提交执行
并发容器ConcurrentHashMapCopyOnWriteArrayListConcurrentLinkedQueue高性能线程安全集合
阻塞队列BlockingQueue接口、ArrayBlockingQueueLinkedBlockingQueue支持阻塞的生产者-消费者模式
同步工具CountDownLatchSemaphoreCyclicBarrierExchanger线程协调同步
Future模式Future接口、Callable接口支持有返回值的异步任务

2.4 设计哲学:分离与抽象

JUC的设计体现了三大核心理念:

  • 分离锁与同步机制:将锁(Lock)、条件(Condition)、原子操作(Atomic)、并发容器等组件解耦,按需使用
  • CAS无锁优化:基于CPU原语的CAS(Compare-And-Swap)实现无锁并发,减少线程阻塞开销
  • 线程池化:通过线程池管理线程生命周期,避免频繁创建/销毁线程的性能损耗

第三章:JUC的演进之路——JDK 6到21

3.1 JDK 6(2006年):基础优化与Fork/Join雏形

JDK 6的主要贡献在于巩固和完善:

  • 锁优化:对ReentrantLock内部实现进行优化,性能提升
  • 并发容器增强:优化ConcurrentHashMap的迭代器,支持更高效的遍历
  • Fork/Join框架雏形:引入ForkJoinPoolForkJoinTask的早期版本(JSR 166y)
  • Deque双端队列:与JUC容器结合,提供ConcurrentLinkedDeque

3.2 JDK 7(2011年):Fork/Join框架完善与Phaser登场

JDK 7为JUC带来了重要增强:

新增组件作用
ForkJoinPool正式发布作为ExecutorService的实现,支持工作窃取(work-stealing)算法,极大提升了并行计算效率
ConcurrentHashMap分段锁优化改进并发控制机制
TransferQueue接口LinkedTransferQueue实现,支持生产者等待消费者消费
Phaser同步器多阶段栅栏,相当于CyclicBarrier的升级版,可用于分阶段任务的并发控制执行

Fork/Join框架的核心思想:将大任务拆分为小任务,并行执行,最后合并结果。工作窃取机制让空闲线程主动从繁忙线程的队列尾部窃取任务,实现负载均衡。

Phaser的特点:支持树形结构以减少并发带来的竞争,可以动态增减参与者,适用于分阶段任务。

3.3 JDK 8(2014年):性能革命与函数式增强【里程碑】

JDK 8为JUC带来了大量重要更新,是继JDK 5之后最重要的版本:

3.3.1 ConcurrentHashMap的重构

  • 放弃分段锁:JDK 7及之前使用Segment分段锁数组,JDK 8改用CAS + synchronized锁桶的方式
  • 链表转红黑树:当链表长度超过阈值(默认为8)且数组长度大于64时,链表转换为红黑树,最坏情况查询效率从O(n)优化为O(log n)
  • 读操作无锁:读操作完全不加锁,通过volatile保证可见性

3.3.2 原子类增强

  • LongAdder:针对高并发计数场景优化,采用分段累加策略,最后汇总,性能远超AtomicLong
  • LongAccumulator:支持自定义累加函数的原子累加器
  • DoubleAdderDoubleAccumulator

3.3.3 CompletableFuture的引入

  • 提供了异步回调能力,支持函数式编程风格
  • 可以组合多个异步任务(thenApplythenCombinethenCompose
  • 支持异常处理(exceptionallyhandle

3.3.4 StampedLock的引入

  • 三种锁模式:写锁、悲观读锁、乐观读锁
  • 乐观读:无锁读取,通过版本戳校验数据一致性,性能远超读写锁

3.3.5 ConcurrentHashMap新方法

  • forEach系列forEachforEachKeyforEachValue
  • search系列searchsearchKeyssearchValues
  • reduce系列reducereduceKeysreduceValues

3.4 JDK 9(2017年):响应式流支持

JDK 9为JUC引入了响应式编程的支持:

  • Flow API:实现了响应式流规范(Reactive Streams),包含PublisherSubscriberSubscriptionProcessor四个核心接口
  • 线程池增强:优化ForkJoinPool的并发管理
  • 并发容器优化ConcurrentHashMapmappingCount()等方法改进

3.5 JDK 10-16:持续优化

版本主要改进
JDK 10性能调优,改进线程池管理
JDK 11(LTS)优化原子类性能,废弃部分过时API
JDK 12-15持续的性能优化和微调
JDK 16Stream.toList()简化收集操作

3.6 JDK 17(2021年):LTS性能优化

JDK 17作为另一个LTS版本,进一步优化了JUC的性能:

  • 并发性能提升:优化ConcurrentHashMap的迭代器实现
  • 内存分配优化:减少对象创建时的内存碎片,提高GC效率
  • ForkJoinPool增强:支持更高效的并行计算
  • java.util.concurrent包优化:进一步提升了高并发环境下的吞吐量

3.7 JDK 19-21:虚拟线程适配【里程碑】

JDK 19(2022年):

  • 虚拟线程预览:Project Loom成果,轻量级线程
  • JUC组件适配ExecutorServiceLockSemaphore等适配虚拟线程

JDK 21(2023年):

  • 虚拟线程正式发布:JUC包全面适配虚拟线程
  • 结构化并发(预览)StructuredTaskScope,将相关任务组织成整体

第四章:JUC演进全景图

4.1 发展时间线

年份JDK版本核心演进关键特性
1990s-Doug Lea开发util.concurrent并发工具库雏形
20045JUC诞生【里程碑】ReentrantLockConcurrentHashMap、原子类、线程池、BlockingQueueCountDownLatch
20066基础优化Fork/Join雏形、ConcurrentLinkedDeque
20117Fork/Join完善ForkJoinPool正式发布、TransferQueuePhaser
20148性能革命【里程碑】ConcurrentHashMap重构、LongAdderCompletableFutureStampedLockreduce系列方法
20179响应式流Flow API(响应式流)
202117LTS性能优化并发性能提升、内存分配优化
202321虚拟线程适配【里程碑】虚拟线程正式发布、结构化并发预览

4.2 版本特性速查表

版本新增特性
JDK 5JUC诞生,核心组件全部引入
JDK 6Fork/Join雏形,性能优化
JDK 7ForkJoinPoolTransferQueuePhaserConcurrentLinkedDeque
JDK 8LongAdderCompletableFutureStampedLockConcurrentHashMap重写
JDK 9Flow API(响应式流)
JDK 21虚拟线程适配、结构化并发预览

第五章:JUC核心组件全景图

5.1 JUC包组织结构

包路径主要内容典型类型
java.util.concurrent并发工具类、线程池、并发容器、同步工具ThreadPoolExecutorConcurrentHashMapCountDownLatchCyclicBarrierSemaphoreExchangerPhaserBlockingQueue系列、ForkJoinPool
java.util.concurrent.atomic原子操作工具类AtomicIntegerAtomicLongAtomicReferenceLongAdderDoubleAdderLongAccumulatorAtomicStampedReference
java.util.concurrent.locks锁机制工具类LockReentrantLockReadWriteLockReentrantReadWriteLockStampedLockCondition

5.2 核心设计:AQS(AbstractQueuedSynchronizer)

AQS是JUC的基石,为锁和同步器提供了底层支持:

核心组成

  • volatile int state:表示同步状态(如锁的占有次数、剩余许可数)
  • CLH队列变体:FIFO等待队列,存储等待获取同步状态的线程
  • 独占/共享模式:支持独占(如ReentrantLock)和共享(如Semaphore)两种模式

工作机制

  • 子类通过重写tryAcquiretryRelease等方法实现自定义同步逻辑
  • AQS提供了acquirerelease模板方法,包含线程排队、阻塞唤醒等通用逻辑

基于AQS的同步器

同步器使用模式作用
ReentrantLock独占可重入互斥锁,state表示锁的重入次数
Semaphore共享信号量,state表示剩余许可数
CountDownLatch共享倒计时门闩,state表示倒计数
ReentrantReadWriteLock独占+共享读写锁,高16位读锁,低16位写锁
ThreadPoolExecutor-内部Worker基于AQS实现

5.3 锁机制(Locks)

锁类型特点适用场景
ReentrantLock可重入、可中断、可设置公平/非公平、支持超时需要灵活锁控制的场景
ReentrantReadWriteLock读读共享、读写互斥、写写互斥读多写少的缓存场景
StampedLockJDK 8新增,支持乐观读,性能优于读写锁超高并发读、写相对较少的场景

ReentrantLock的核心特性

  • 可重入:允许线程多次获得同一个锁
  • 可中断:可以在等待锁时响应中断
  • 公平性控制:公平锁保证锁的获取顺序按照请求顺序分配,避免线程“饥饿”

5.4 原子操作类(Atomic)

基于CAS(Compare-And-Swap)实现的无锁原子操作:

类名作用核心方法
AtomicInteger原子更新intincrementAndGet()compareAndSet()
AtomicLong原子更新long同上
AtomicBoolean原子更新booleancompareAndSet()
AtomicReference<V>原子更新对象引用compareAndSet()getAndSet()
AtomicStampedReference<V>解决CAS ABA问题带版本戳的引用更新
LongAdderJDK 8,高并发累加器increment()sum()

CAS原理:依托CPU原子指令实现的无锁原子操作,通过对比共享变量的预期值与内存值,仅在相等时更新为新值。

5.5 线程池(Executor)

线程池的核心是ThreadPoolExecutor,它的构造函数包含7个核心参数:

public ThreadPoolExecutor(
    int corePoolSize,           // 核心线程数(常驻)
    int maximumPoolSize,        // 最大线程数
    long keepAliveTime,         // 非核心线程空闲超时时间
    TimeUnit unit,              // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

工作流程

  1. 提交任务:调用execute()submit()
  2. 判断核心线程池:如果当前线程数 < corePoolSize,创建新线程执行任务
  3. 进入队列:如果当前线程数 ≥ corePoolSize,尝试将任务放入workQueue
  4. 扩容:如果队列已满且当前线程数 < maximumPoolSize,创建新线程执行任务
  5. 拒绝策略:如果队列已满且线程数已达maximumPoolSize,执行拒绝策略

Executors工厂类提供的快捷创建方式

线程池类型特点适用场景
newFixedThreadPool(n)固定线程数,无界队列任务量稳定的场景
newCachedThreadPool()动态扩容,60秒超时短期、高频任务
newSingleThreadExecutor()单线程,串行执行任务需顺序执行
newScheduledThreadPool(n)定时/周期性任务延迟执行、定时任务

5.6 并发容器(Collections)

容器类线程安全机制特点适用场景
ConcurrentHashMapCAS + synchronized锁桶高并发读写,JDK 8红黑树优化高性能并发映射
CopyOnWriteArrayList写时复制读无锁,写创建新副本读多写少(如监听器列表)
CopyOnWriteArraySet基于CopyOnWriteArrayList同左读多写少的Set
ConcurrentLinkedQueueCAS无锁无界、非阻塞、高效高并发生产消费
ConcurrentLinkedDequeCAS无锁支持双端操作双端队列场景
**BlockingQueue**实现锁+条件等待支持阻塞的put/take线程池、生产者-消费者

阻塞队列的主要实现

队列实现特点
ArrayBlockingQueue有界数组阻塞队列
LinkedBlockingQueue无界(默认)链表阻塞队列,读写锁分离
PriorityBlockingQueue无界优先队列
SynchronousQueue无容量,直接传递
DelayQueue延迟元素队列
LinkedTransferQueue支持生产者等待消费者消费

5.7 同步工具类(Tools)

工具类作用典型用法
CountDownLatch计数器,等待多个线程完成主线程等待所有子线程初始化
CyclicBarrier循环屏障,等待线程达到阈值后一起执行多线程分阶段计算
Semaphore信号量,控制并发访问数限流(同时最多N个线程访问)
Exchanger<V>线程间数据交换两个线程交换数据
Phaser多阶段栅栏替代CountDownLatch+CyclicBarrier

5.8 线程协作:Condition

ConditionLock的配套工具,替代Object.wait()/notify(),支持多条件等待

ReentrantLock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition(); // 非空条件
Condition notFull = lock.newCondition();  // 非满条件

// 生产者
lock.lock();
try {
    while (队列满) {
        notFull.await(); // 等待非满
    }
    入队();
    notEmpty.signal(); // 唤醒非空等待的消费者
} finally {
    lock.unlock();
}

5.9 Future与CompletableFuture

  • Callable<V>:有返回值的任务,与Runnable并列
  • Future<V>:代表异步任务的结果,支持get()阻塞获取、cancel()取消
  • CompletableFuture:JDK 8引入,支持异步回调、任务组合、异常处理

第六章:设计哲学与实现原理

6.1 CAS(Compare-And-Swap)原理

CAS是JUC的底层基石,依托CPU原子指令实现的无锁原子操作。其核心逻辑是:

boolean compareAndSet(expectedValue, updateValue);

当希望修改的值与expectedValue相同时,则尝试将值更新为updateValue,更新成功返回true,否则返回false

CAS的优势:无锁并发,减少线程阻塞开销 CAS的局限:ABA问题,可通过AtomicStampedReference解决

6.2 AQS的设计模式

AQS采用了模板方法设计模式

  • 顶层定义了同步器的基本骨架:状态管理、线程排队、阻塞唤醒
  • 子类通过实现tryAcquiretryRelease等方法定制具体同步逻辑

这种设计使得开发者可以基于AQS快速实现各种自定义同步器,极大地提高了框架的可扩展性。

6.3 并发容器的演进

版本ConcurrentHashMap演进
JDK 5Segment分段锁(默认16段)
JDK 7优化分段锁,性能提升
JDK 8CAS + synchronized锁桶,红黑树优化,读操作无锁

第七章:面试题库

5道难度递增的基础面试题

第1题:JUC是什么?它在哪个JDK版本中引入?(难度⭐)

参考答案: JUC是java.util.concurrent包的简称,是Java专门用于并发编程的工具包。它包含了锁、原子类、线程池、并发容器、同步工具等核心组件。

它在JDK 5中正式引入,由并发领域泰斗Doug Lea主导设计和实现,是JSR 166规范的具体实现。

第2题:synchronizedReentrantLock有什么区别?(难度⭐⭐)

参考答案

维度synchronizedReentrantLock
实现层面JVM内置关键字Java类库实现
锁释放自动释放必须手动unlock()
锁获取灵活性只能阻塞获取支持tryLock()尝试获取、超时获取
中断响应无法响应中断支持lockInterruptibly()响应中断
公平性非公平可配置公平/非公平
条件等待wait()/notify()Condition,支持多条件
适用场景锁竞争不激烈时简单易用需要灵活控制、超时、公平锁时

第3题:ConcurrentHashMap在JDK 8中做了哪些重要改进?(难度⭐⭐⭐)

参考答案: JDK 8对ConcurrentHashMap进行了重大重构:

  1. 放弃分段锁:JDK 7使用Segment数组分段锁,JDK 8改为使用CAS + synchronized锁桶的方式,降低锁粒度
  2. 链表转红黑树:当链表长度超过8且数组长度大于64时,链表转换为红黑树,查询效率从O(n)优化为O(log n)
  3. 读操作无锁:读操作完全不加锁,通过volatile保证可见性
  4. 支持新的函数式方法forEachsearchreduce系列方法
  5. 原子复合操作computeIfAbsentcomputeIfPresentmerge

第4题:AQS是什么?它如何支撑JUC的同步器?(难度⭐⭐⭐⭐)

参考答案: AQS(AbstractQueuedSynchronizer)是JUC的基石,为锁和同步器提供了底层支持。

核心组成

  • volatile int state:表示同步状态(如锁的占有次数、剩余许可数)
  • CLH队列变体:FIFO等待队列,存储等待获取同步状态的线程
  • 独占/共享模式:支持独占(如ReentrantLock)和共享(如Semaphore)两种模式

工作机制

  • 子类通过重写tryAcquiretryRelease等方法实现自定义同步逻辑
  • AQS提供了acquirerelease模板方法,包含线程排队、阻塞唤醒等通用逻辑

基于AQS的同步器

  • ReentrantLock:独占模式,state表示锁的重入次数
  • Semaphore:共享模式,state表示剩余许可数
  • CountDownLatch:共享模式,state表示倒计数
  • ReentrantReadWriteLock:混合模式,高16位读锁,低16位写锁

第5题:ThreadPoolExecutor的核心参数有哪些?线程池的工作流程是怎样的?(难度⭐⭐⭐⭐)

参考答案

public ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数(常驻)
    int maximumPoolSize,   // 最大线程数
    long keepAliveTime,    // 非核心线程空闲超时时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,       // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

工作流程

  1. 提交任务:调用execute()submit()
  2. 判断核心线程池:如果当前线程数 < corePoolSize,创建新线程执行任务
  3. 进入队列:如果当前线程数 ≥ corePoolSize,尝试将任务放入workQueue
  4. 扩容:如果队列已满且当前线程数 < maximumPoolSize,创建新线程执行任务
  5. 拒绝策略:如果队列已满且线程数已达maximumPoolSize,执行拒绝策略

拒绝策略

  • AbortPolicy(默认):抛出RejectedExecutionException
  • CallerRunsPolicy:调用者线程自己执行任务
  • DiscardPolicy:直接丢弃,不抛出异常
  • DiscardOldestPolicy:丢弃队列中最老的任务,重新提交新任务

3道实战场景题

场景1:实现一个限流器,最多允许10个线程同时访问

问题:你需要实现一个服务接口,要求最多允许10个线程同时访问,超出限制的线程需要等待,直到有线程释放资源。你会如何用JUC实现?

考察点Semaphore的应用

参考思路

@Service
public class RateLimitedService {
    private final Semaphore semaphore = new Semaphore(10); // 最多10个许可
    
    public void processRequest(Request request) {
        try {
            // 尝试获取许可,可设置超时
            if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
                throw new RuntimeException("系统繁忙,请稍后重试");
            }
            try {
                // 执行业务逻辑
                doProcess(request);
            } finally {
                semaphore.release(); // 释放许可
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("被中断", e);
        }
    }
}

场景2:多线程分批处理数据

问题:你有100万个数据需要处理,每个数据处理耗时约1秒。为了提高效率,你决定使用10个线程并行处理。需要等所有线程都完成一批数据的准备后,再同时开始处理,然后继续下一批。如何实现?

考察点CyclicBarrier的应用

参考思路

public class BatchProcessor {
    private static final int THREAD_COUNT = 10;
    private static final int BATCH_SIZE = 10;
    private final CyclicBarrier barrier;
    private final List<List<Data>> batches;
    
    public BatchProcessor(List<Data> allData) {
        // 将数据分批
        batches = new ArrayList<>();
        for (int i = 0; i < allData.size(); i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, allData.size());
            batches.add(allData.subList(i, end));
        }
        
        // 初始化屏障:所有线程都准备好后执行屏障动作
        barrier = new CyclicBarrier(THREAD_COUNT, () -> {
            System.out.println("所有线程准备就绪,开始处理第 " + (currentBatchIndex + 1) + " 批");
        });
    }
    
    public void process() {
        for (int i = 0; i < THREAD_COUNT; i++) {
            int threadId = i;
            new Thread(() -> {
                for (int batchIdx = threadId; batchIdx < batches.size(); batchIdx += THREAD_COUNT) {
                    // 获取当前线程要处理的数据批
                    List<Data> batch = batches.get(batchIdx);
                    
                    // 准备数据(如预处理)
                    prepareData(batch);
                    
                    try {
                        // 等待所有线程准备就绪
                        barrier.await();
                        
                        // 同时开始处理
                        processBatch(batch);
                        
                        // 等待所有线程完成当前批处理,再进入下一批
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

场景3:高性能计数器,支持千万级并发

问题:你需要实现一个PV/UV统计计数器,要求在高并发下性能极高,能够支撑千万级QPS的递增操作。你会选择哪种JUC类?为什么?

考察点LongAdder的优势、与AtomicLong的对比

参考思路

public class HighConcurrencyCounter {
    // 使用LongAdder替代AtomicLong
    private final LongAdder counter = new LongAdder();
    
    public void increment() {
        counter.increment(); // 性能极高,内部采用分段累加
    }
    
    public long getCount() {
        return counter.sum(); // 汇总所有分段的值
    }
}

为什么选择LongAdder

  • AtomicLong的瓶颈:在高并发下,大量线程同时CAS更新同一个变量,会导致严重的自旋竞争和缓存行失效
  • LongAdder的设计
    • 内部维护一个Cell数组(分段累加器)
    • 每个线程映射到不同的Cell进行累加,减少竞争
    • 最后通过sum()汇总所有Cell的值
  • 性能对比:在超高并发下,LongAdder的性能可以是AtomicLong的数倍甚至数十倍

结语

从JDK 5的初生牛犊,到如今JDK 21的成熟稳健,JUC已经走过了二十年的辉煌历程。它始于并发大师Doug Lea的杰作,在泛型与函数式编程的浪潮中不断进化,在多核时代开疆拓土,在虚拟线程革命中焕发新颜。它不仅是Java并发编程的基石,更是无数高并发系统的技术支柱。

理解JUC的历史与核心原理,不仅能让你在面试中游刃有余,更能让你在面对复杂的并发场景时,做出最恰当的技术选择,写出高性能、高可靠的多线程代码。

参考资料

  1. 百度智能云:J.U.C并发包背后的故事:从JCP到Lock接口的实现
  2. CSDN博客:从入门到实战:Java JUC 并发工具包完全指南
  3. UCloud:Java多线程进阶(一)—— J.U.C并发包概述
  4. 华为云社区:JUC 核心组件全景:Lock、Atomic、AQS!
  5. 清华大学出版社:《Java高并发核心编程》前言
  6. 51CTO博客:java的基本操作 java的juc
  7. 阿里云开发者社区:【多线程面试题二十四】说说你对JUC的了解

知识点测试

读完文章了?来测试一下你对知识点的掌握程度吧!

评论区

使用 GitHub 账号登录后即可发表评论,支持 Markdown 格式。

如果评论系统无法加载,请确保:

  • 您的网络可以访问 GitHub
  • giscus GitHub App 已安装到仓库
  • 仓库已启用 Discussions 功能