多线程(七)AQS

  AQS(AbstractQueuedSynchronizer,抽象队列同步器) 是 Java 并发包(java.util.concurrent)的绝对核心ReentrantLockCountDownLatchSemaphoreReentrantReadWriteLock 等底层全部依赖 AQS

一、 核心思想:模板方法模式

  • AQS 负责:维护同步状态(state)、管理线程的排队与阻塞、唤醒。

  • 子类负责:定义什么是“获取锁成功”和“释放锁成功”(即如何操作 state)。

  AQS 定义了几个关键方法,子类可以选择性重写(以下方法在 AQS 中默认抛出 UnsupportedOperationException,强制子类按需实现):

tryAcquire(int) 独占模式获取锁(如 ReentrantLock
tryRelease(int) 独占模式释放锁
tryAcquireShared(int) 共享模式获取锁(如 SemaphoreCountDownLatch
tryReleaseShared(int) 共享模式释放锁
isHeldExclusively() 是否被当前线程独占
protected boolean isHeldExclusively() {
     throw new UnsupportedOperationException();
}

二、 核心结构

示例代码取自JDK21

1. volatile int state(同步状态)

    /**
     * The synchronization state.
     */
    private volatile int state;

  不同的工具赋予它不同的含义:

  • ReentrantLockstate = 0 表示无锁,state > 0 表示锁被重入次数。【独占模式

final boolean tryLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, 1)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (getExclusiveOwnerThread() == current) {
                if (++c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            }
            return false;
        }
  • Semaphorestate 表示可用许可数量。

  • CountDownLatchstate 表示剩余计数。【共享模式


/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

2. FIFO 双向队列

  当线程获取锁失败时,会被包装成 Node 节点,加入队列尾部等待。

abstract static class Node {
        volatile Node prev;       // initially attached via casTail
        volatile Node next;       // visibly nonnull when signallable
        Thread waiter;            // visibly nonnull when enqueued
        volatile int status;      // written by owner, atomic bit ops by others

        // methods for atomic operations
        final boolean casPrev(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, PREV, c, v);
        }
        final boolean casNext(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, NEXT, c, v);
        }
        final int getAndUnsetStatus(int v) {     // for signalling
            return U.getAndBitwiseAndInt(this, STATUS, ~v);
        }
        final void setPrevRelaxed(Node p) {      // for off-queue assignment
            U.putReference(this, PREV, p);
        }
        final void setStatusRelaxed(int s) {     // for off-queue assignment
            U.putInt(this, STATUS, s);
        }
        final void clearStatus() {               // for reducing unneeded signals
            U.putIntOpaque(this, STATUS, 0);
        }

        private static final long STATUS
            = U.objectFieldOffset(Node.class, "status");
        private static final long NEXT
            = U.objectFieldOffset(Node.class, "next");
        private static final long PREV
            = U.objectFieldOffset(Node.class, "prev");
    }
View Code

  JDK 21 AQS Node 的核心变化

 
字段JDK 8JDK 21变化原因
prev volatile Node prev volatile Node prev 保持不变
next volatile Node next volatile Node next 保持不变
线程字段 volatile Thread thread Thread waiter(去 volatile 利用 prev/next 的 volatile 保证可见性,减少开销
状态字段 volatile int waitStatus volatile int status(支持位运算 支持更复杂的状态组合与原子位操作

3. Unsafe + CAS(原子操作)

  AQS 使用 Unsafe.compareAndSwapInt 等底层 CAS 操作来修改 state 和队列指针,保证线程安全。

protected final boolean compareAndSetState(int expect, int update) {
        return U.compareAndSetInt(this, STATE, expect, update);
    }

  AQS 内部有一个 volatile int state 表示同步状态,和一个 FIFO 队列管理等待线程。由于 volatile 只保证可见性不保证原子性,AQS 通过调用 Unsafe 类提供的 CAS 方法(底层是 CPU 的 cmpxchg 指令),以无锁、原子的方式修改 state(抢锁)和队列指针(入队/出队)。CAS 成功则继续执行,失败则自旋重试或进入队列等待。这种设计避免了 synchronized 重量级锁的挂起/唤醒开销,是 AQS 高性能的根本原因。

三、 编码实例

ReentrantLock 

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockBasicDemo {
    // 定义锁(通常作为成员变量,static final 或实例字段)
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();  // 加锁
        try {
            count++;  // 临界区代码
        } finally {
            lock.unlock();  // 必须放在 finally 中,防止异常导致死锁
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockBasicDemo demo = new ReentrantLockBasicDemo();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) t.join();
        System.out.println("最终 count = " + demo.getCount()); // 10000
    }
}
View Code

countDownLanch

import java.util.concurrent.CountDownLatch;

public class CountDownLatchBasic {
    public static void main(String[] args) throws InterruptedException {
        int workerCount = 3;
        CountDownLatch latch = new CountDownLatch(workerCount);

        // 启动 3 个工作线程
        for (int i = 1; i <= workerCount; i++) {
            final int id = i;
            new Thread(() -> {
                try {
                    System.out.println("工人" + id + " 开始工作...");
                    Thread.sleep(1000 * id);  // 模拟不同耗时
                    System.out.println("工人" + id + " 工作完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();  // 关键:完成后计数减 1
                }
            }).start();
        }

        System.out.println("主线程等待所有工人完成...");
        latch.await();  // 阻塞,直到计数器归零
        System.out.println("所有工人完成,主线程继续执行");
    }
}
View Code

 

 

 
 
posted @ 2026-09-14 10:34  鄙人取个名字好难  阅读(3)  评论(0)    收藏  举报