此篇主要针对大家感兴趣的AQS详细说明,欢迎评论区指正!

整个《Java并发编程研读》专栏 如下

《Java并发编程研读》第一章:线程基础

一、AQS究竟是什么?

AQS(AbstractQueuedSynchronizer) 不是一个具体的锁,而是一个构建锁和同步器的框架。你可以把它想象成汽车工厂的"底盘平台":同样的底盘可以造出轿车、SUV、卡车等各种车型。

二、为什么需要AQS?解决了什么问题?

1. 问题背景

在AQS出现之前,Java只有synchronized关键字实现同步,但它存在局限:

  • 无法实现公平锁

  • 没有超时等待机制

  • 没有条件变量的精细控制

  • 无法中断一个正在等待的线程

2. AQS的解决方案

AQS提供了一个通用的并发控制框架,让开发者可以:

  • 自定义各种锁策略

  • 实现复杂的同步需求

  • 获得更好的性能

三、AQS的架构和工作原理

核心结构:CLH队列 + 状态变量

关键代码:AQS核心方法

// AQS最核心的三个部分:
public abstract class AbstractQueuedSynchronizer {
    // 1. 同步状态 - 核心资源计数器
    private volatile int state;
    
    // 2. 等待队列的头尾指针
    private transient volatile Node head;
    private transient volatile Node tail;
    
    // 3. 节点类 - 封装等待线程
    static final class Node {
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        volatile int waitStatus;
        // ... 其他字段
    }
}

AQS 的两种同步模式

独占模式(Exclusive Mode)
  • 特点:同一时刻仅允许一个线程持有资源。
  • 典型应用ReentrantLockCountDownLatch
  • 核心方法
    • acquire(int arg):获取资源(失败则入队阻塞)。
    • release(int arg):释放资源(唤醒后继节点)。

acquire 流程

 共享模式(Shared Mode)
  • 特点:允许多个线程同时持有资源(如读锁、信号量)。
  • 典型应用SemaphoreReentrantReadWriteLock 的读锁。
  • 核心方法
    • acquireShared(int arg):获取共享资源。
    • releaseShared(int arg):释放共享资源(可能唤醒多个后继)。
releaseShared 的传播性
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    unparkSuccessor(h); // 唤醒后继
            }
            else if (ws == 0)
                compareAndSetWaitStatus(h, 0, Node.PROPAGATE);
        }
        if (h == head) break; // 若head未变则退出
    }
}

四、实际开发中如何与AQS交互

场景1:使用现成的AQS实现

普通开发不需要直接使用AQS,而是使用基于AQS的工具类:

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

public class AQSInPractice {
    public static void main(String[] args) throws InterruptedException {
        // 1. ReentrantLock - 可重入锁
        ReentrantLock lock = new ReentrantLock(true); // true表示公平锁
        lock.lock();
        try {
            // 临界区代码
            System.out.println("锁已获取");
        } finally {
            lock.unlock();
        }
        
        // 2. CountDownLatch - 线程协调
        CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                // 做一些工作
                latch.countDown();
            }).start();
        }
        latch.await(); // 等待所有线程完成
        System.out.println("所有任务完成");
        
        // 3. Semaphore - 信号量
        Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
        semaphore.acquire();
        try {
            // 访问共享资源
        } finally {
            semaphore.release();
        }
    }
}

场景2:扩展AQS实现自定义同步器

可以继承AQS实现自定义同步需求:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

// 一个简单的"最多允许N个线程访问"的自定义锁
public class SimpleLimitLock {
    private final Sync sync;
    
    public SimpleLimitLock(int limit) {
        sync = new Sync(limit);
    }
    
    // 内部Sync类继承AQS
    private static class Sync extends AbstractQueuedSynchronizer {
        Sync(int limit) {
            setState(limit); // 初始化状态为允许的最大线程数
        }
        
        // 尝试获取锁(共享模式)
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                // 如果剩余许可不足或CAS成功,返回结果
                if (remaining < 0 || 
                    compareAndSetState(available, remaining)) {
                    return remaining;
                }
            }
        }
        
        // 尝试释放锁(共享模式)
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (compareAndSetState(current, next)) {
                    return true;
                }
            }
        }
    }
    
    public void lock() {
        sync.acquireShared(1);
    }
    
    public void unlock() {
        sync.releaseShared(1);
    }
}

五、AQS在Java并发体系中的角色

// Java并发编程的层次结构:
┌─────────────────────────────────────────────────┐
│             Java并发应用层                        │
│  • 线程池 (ExecutorService)                     │
│  • 并发集合 (ConcurrentHashMap)                 │
│  • 异步任务 (CompletableFuture)                 │
├─────────────────────────────────────────────────┤
│             AQS工具类层                          │
│  • ReentrantLock                                │
│  • Semaphore                                    │
│  • CountDownLatch                               │
│  • CyclicBarrier                                │
├─────────────────────────────────────────────────┤
│             AQS框架层                            │
│  • 状态管理 (state)                             │
│  • 队列管理 (CLH队列)                           │
│  • 线程阻塞/唤醒 (LockSupport)                  │
├─────────────────────────────────────────────────┤
│             JVM/OS层                            │
│  • synchronized监视器锁                         │
│  • CAS指令 (Unsafe类)                           │
│  • 线程调度 (操作系统)                          │
└─────────────────────────────────────────────────┘

六、AQS的工作流程示例

以ReentrantLock的加锁过程为例:

// 伪代码展示AQS工作流程
class ReentrantLock {
    final Sync sync;
    
    public void lock() {
        sync.acquire(1); // 调用AQS的模板方法
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // AQS.acquire()的调用链:
        // 1. tryAcquire() 尝试获取锁(子类实现)
        // 2. addWaiter() 创建节点加入队列
        // 3. acquireQueued() 在队列中自旋/阻塞
        // 4. 如果被中断过,自我中断恢复状态
    }
}

具体过程可以用图表示:

七、实际开发中的最佳实践

1. 选择合适的同步工具

// 不同场景使用不同的AQS工具
public class SyncToolSelection {
    
    // 场景1:需要可重入、公平性的互斥锁
    void useReentrantLock() {
        ReentrantLock lock = new ReentrantLock(true); // 公平锁
        // 适用于资源竞争激烈的场景
    }
    
    // 场景2:控制并发线程数
    void useSemaphore() {
        Semaphore semaphore = new Semaphore(10); // 最多10个线程同时访问
        // 适用于数据库连接池、限流场景
    }
    
    // 场景3:等待多个任务完成
    void useCountDownLatch() {
        CountDownLatch latch = new CountDownLatch(5);
        // 适用于启动阶段等待初始化完成
    }
    
    // 场景4:循环等待
    void useCyclicBarrier() {
        CyclicBarrier barrier = new CyclicBarrier(4);
        // 适用于并行计算,等待所有线程到达屏障
    }
}

2. 避免常见陷阱

public class AQSPitfalls {
    // 陷阱1:忘记释放锁
    void wrongWay() {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        try {
            // 业务代码
            if (someCondition) {
                return; // 这里直接返回,忘记解锁!
            }
            // 更多代码
        } finally {
            lock.unlock(); // 必须在finally中释放
        }
    }
    
    // 陷阱2:错误使用条件变量
    void conditionVariableMistake() {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        
        // 错误:没有在lock保护下调用await
        // condition.await(); 
        
        // 正确:
        lock.lock();
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
    }
}

八、总结:AQS在并发编程中的角色

  1. 框架提供者:为锁和同步器提供通用实现框架

  2. 基础设施:是Java并发包的基石,如大楼的地基

  3. 性能优化:相比synchronized提供更灵活的优化空间

  4. 功能扩展:支持公平/非公平、可重入、读写分离等高级特性

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐