一文搞懂Java并发工具(LockSupport/AQS/锁/同步器/阻塞队列/线程池)

前言

Java并发编程是后端面试核心重难点,java.util.concurrent包提供了一整套成熟并发工具,从底层线程阻塞工具LockSupport、同步器AQS,到各类锁、同步等待工具、阻塞队列、线程池,层层依赖、环环相扣。
本文按照底层→中层→上层完整链路梳理,吃透原理、对比差异、附实战代码,面试、开发一套搞定。

一、底层基石:LockSupport 线程阻塞唤醒工具

1. 核心定位

所有锁、AQS底层依赖的线程阻塞工具,专门负责线程park阻塞、unpark唤醒,替代传统wait/notify,解决原生监视器锁诸多痛点。
核心机制:许可模型,每个线程独有许可槽,只有0/1两种状态,不可累加。

2. 许可执行逻辑

  1. park():无许可则阻塞;有许可则消耗许可(置0)直接放行;
  2. unpark(Thread t):给指定线程发放许可,槽为0则改为1,已有1无任何操作;
  3. 优势:支持先唤醒后阻塞,不会丢失唤醒信号。

3. LockSupport vs Object.wait/notify

对比维度 LockSupport wait/notify
调用前提 无需持有任何锁 必须包裹synchronized
唤醒精度 精准指定目标线程 随机唤醒/全部唤醒
执行顺序 先unpark后park也生效 先notify后wait永久阻塞
解除阻塞 unpark、中断、超时 notify、中断、超时

4. 代码示例

public class LockSupportDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            System.out.println("线程A阻塞等待许可");
            LockSupport.park();
            System.out.println("线程A拿到许可继续执行");
        }, "A");
        threadA.start();
        Thread.sleep(1000);
        new Thread(() -> {
            System.out.println("线程B给A发放许可");
            LockSupport.unpark(threadA);
        }).start();
    }
}

5. 中断特性

park()被线程中断时不会抛出异常,仅直接返回,需手动通过Thread.interrupted()判断中断标记。

二、同步器骨架:AQS 抽象队列同步器

1. 核心思想:模板方法模式

AQS封装线程排队、阻塞唤醒通用逻辑,子类仅需实现少量钩子方法定义「加锁/释放锁」规则,不用重复处理线程阻塞逻辑。
所有JUC锁、同步工具底层均基于AQS:ReentrantLock、读写锁、CountDownLatch、Semaphore

2. 两大核心数据结构

(1)volatile int state 同步状态

state含义由子类自定义,通过CAS原子修改保证线程安全:

  • ReentrantLock:state=重入次数,0无锁;
  • ReentrantReadWriteLock:高16位读计数、低16位写计数;
  • CountDownLatch:倒计时计数器;
  • Semaphore:可用许可数量。

(2)CLH双向同步等待队列

存储抢锁失败的线程,节点Node核心状态waitStatus:

  • SIGNAL(-1):后继线程需要我唤醒;
  • CANCELLED(1):线程取消等待;
  • CONDITION(-2):当前节点在Condition条件队列;
  • PROPAGATE(-3):共享锁唤醒传播;
  • 0:初始状态。

3. 两种工作模式

独占模式(ReentrantLock)

流程:acquire()

  1. tryAcquire()尝试抢锁,成功直接返回;
  2. 失败创建EXCLUSIVE节点入CLH队列;
  3. acquireQueued循环:前驱是头节点则重试抢锁,否则LockSupport.park()阻塞;
  4. 被唤醒后重复抢锁逻辑。

钩子方法:tryAcquire/tryRelease/isHeldExclusively

共享模式(CountDownLatch/Semaphore)

流程:releaseShared()

  1. tryReleaseShared释放资源,state变更成功;
  2. doReleaseShared唤醒后继节点,共享锁会链式传播唤醒所有等待线程。

钩子方法:tryAcquireShared/tryReleaseShared

4. 自定义同步器开发步骤

  1. 继承AbstractQueuedSynchronizer
  2. 根据独占/共享重写对应钩子;
  3. 对外封装lock/unlock等业务API。

三、独占锁:ReentrantLock 可重入锁

1. 可重入原理

AQS中exclusiveOwnerThread记录持有锁线程,同一线程重复加锁时state自增,释放时递减至0才完全释放,支持多层嵌套。

2. 公平锁 vs 非公平锁

  1. 非公平锁(默认,性能更高)
    抢锁前不检查等待队列,直接CAS抢占,减少线程切换开销;
  2. 公平锁
    加锁前校验CLH队列是否有前驱等待线程,有则排队,严格FIFO。

3. Condition 多条件等待机制

一把锁可创建多个独立Condition,解决synchronized单等待队列只能随机唤醒的缺陷。

底层结构

每个Condition维护单向条件队列(节点waitStatus=-2),与CLH同步队列隔离。

执行流程

  1. await():释放全部锁,线程加入当前Condition条件队列阻塞;
  2. signal():取出条件队列头节点,迁移至CLH同步队列等待抢锁;
  3. signalAll():迁移条件队列全部节点到同步队列。

Condition vs wait/notify

维度 Condition wait/notify
等待队列 多条件队列,精准唤醒 仅有1个隐式队列
唤醒粒度 仅唤醒当前条件线程 随机/全部唤醒
依赖 ReentrantLock synchronized监视器

4. ReentrantLock 与 synchronized 对比

对比 ReentrantLock synchronized
实现 Java层AQS JVM底层C++
重入计数 可通过API获取 无对外接口
释放 必须手动unlock 代码块自动释放
拓展 公平锁、多Condition、可中断 功能单一

四、读写锁:ReentrantReadWriteLock

1. 设计目标

读多写少场景提升并发:读共享、写独占,多线程可同时读,写操作阻塞所有读写。

2. state位拆分

32位int state高低16位拆分:

  • 低16位:写锁重入计数;
  • 高16位:全局读锁总计数;
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << 16) - 1;
// 获取写计数:state & EXCLUSIVE_MASK
// 获取读计数:state >>> SHARED_SHIFT

3. 读写锁重入实现

  1. 写锁:同ReentrantLock,state低16位计数;
  2. 读锁:ThreadLocal<HoldCounter>记录单线程读重入次数,高16位全局累加。

4. 锁降级(允许)、锁升级(禁止)

  1. 锁降级(写→读)
    持有写锁时获取读锁,再释放写锁;保证自己修改的数据对后续读线程可见,无死锁。
  2. 锁升级(读→写)
    两个线程同时持有读锁,都等待对方释放读锁才能获取写锁,互相阻塞,永久死锁,JDK禁止。

五、三大同步等待工具(CountDownLatch / CyclicBarrier / Semaphore)

1. CountDownLatch 倒计时门栓

核心能力

主线程等待N个子线程全部完成,一次性不可重置,共享AQS实现。

  1. 初始化state=N;
  2. countDown():CAS递减state;
  3. await():state>0则阻塞,归零后全部唤醒。

典型场景

  • 服务启动等待缓存、DB、配置初始化;
  • 并发压测统一发令、批量任务结果汇总。

双Latch经典并发示例

CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(N);
// N个线程全部await(start)统一并发;主线程await(done)等待全部结束

2. CyclicBarrier 循环屏障

核心能力

N个线程互相等待,全部到达屏障点后统一放行,可循环复用,支持屏障后置任务。

典型场景

多阶段批量数据处理,每阶段所有线程就绪再执行下一阶段。

3. Semaphore 信号量限流工具

核心能力

控制同时访问资源的最大线程数,底层AQS共享模式,许可可动态增减。

  1. acquire():获取许可,不足则阻塞;
  2. release():归还许可,唤醒等待线程。

典型场景

接口限流、数据库连接池并发控制。

三者核心区别总结

  1. CountDownLatch:一方等多方,一次性;
  2. CyclicBarrier:多方互相等待,可循环;
  3. Semaphore:控制并发资源数量,限流专用。

六、阻塞队列 BlockingQueue

1. 核心特性

线程安全,自带阻塞读写:

  • put(e):队列满阻塞写入线程;
  • take():队列为空阻塞读取线程。

2. ArrayBlockingQueue(有界数组队列)

  1. 固定容量,单把ReentrantLock,读写互斥;
  2. 两个Condition:notEmpty、notFull;
  3. 连续数组内存,并发读写竞争激烈时性能较差。

3. LinkedBlockingQueue(链表队列)

  1. 默认无界,可指定容量;
  2. 双锁设计:takeLock、putLock,读写并行,高并发性能更优;
  3. 动态创建节点,存在内存碎片。

4. 底层阻塞逻辑(ArrayBlockingQueue举例)

// put流程
lock.lock();
try {
    while(队列满) notFull.await();
    入队;
    notEmpty.signal();
} finally { lock.unlock(); }

// take流程
lock.lock();
try {
    while(队列为空) notEmpty.await();
    出队;
    notFull.signal();
} finally { lock.unlock(); }

七、线程池 ThreadPoolExecutor

1. 创建线程池的意义

频繁创建销毁线程存在内核切换开销,线程池复用线程、管控并发,防止线程爆炸耗尽系统资源。

2. 七大核心构造参数

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

3. 任务执行完整流程

  1. 线程数 < corePoolSize:新建核心线程执行;
  2. 核心线程已满:任务存入阻塞队列;
  3. 队列已满:创建非核心线程;
  4. 线程达到maximumPoolSize:执行拒绝策略。

4. 四种拒绝策略

  1. AbortPolicy 默认:直接抛出异常;
  2. CallerRunsPolicy:提交线程自行执行,天然限流回压,生产推荐;
  3. DiscardPolicy:静默丢弃任务;
  4. DiscardOldestPolicy:丢弃队列最旧任务,放入新任务。

5. CompletableFuture 自定义线程池

CompletableFuture无参异步方法默认使用ForkJoinPool,高并发IO场景建议传入自定义ThreadPoolExecutor隔离线程资源,避免公共池阻塞。

八、全文总结

  1. 底层阻塞:LockSupport提供线程park/unpark,是所有同步组件基础;
  2. 同步骨架:AQS通过state+CLH队列定义统一同步模板,独占/共享两种模式;
  3. 锁层:ReentrantLock独占可重入、多Condition精准等待;ReentrantReadWriteLock读写分离优化读多写少;
  4. 同步工具:CountDownLatch(等待完成)、CyclicBarrier(互相等待)、Semaphore(限流);
  5. 阻塞队列:基于ReentrantLock+Condition实现线程安全阻塞读写;
  6. 线程池:复用线程、管控并发,配套拒绝策略防止雪崩。

整套工具自上而下层层依赖,面试常考底层联动原理、各类工具场景对比,开发中根据业务并发模型合理选择即可。

posted @ 2026-07-13 12:00  七星6609  阅读(21)  评论(0)    收藏  举报