3.2 AQS:排队上厕所的哲学(手写简易版ReentrantLock)

190 阅读6分钟

3.2 AQS:排队上厕所的哲学(手写简易版ReentrantLock)


一、AQS是啥?厕所排队神器了解一下

想象一下高峰期的商场厕所,门口永远排着长队。AQS (AbstractQueuedSynchronizer) 就相当于厕所的排队管理系统,它负责:

  1. 维护排队状态:厕所坑位是否空闲 (state变量)
  2. 管理等待队列:谁在排队,排队的顺序 (FIFO队列)
  3. 叫号机制:坑位空了,按顺序叫号 (线程唤醒)

AQS的核心思想状态state + FIFO等待队列

// AbstractQueuedSynchronizer源码核心字段
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer {

    private volatile int state; // 同步状态,厕所坑位状态

    private transient volatile Node head; // FIFO等待队列头节点
    private transient volatile Node tail; // FIFO等待队列尾节点
    ...
}

二、ReentrantLock:厕所带锁豪华版 (手写简易版ReentrantLock)

ReentrantLock 就是基于 AQS 实现的“豪华版厕所”,它比 synchronized 这个“公厕”更灵活,功能更强大。

ReentrantLock 的核心组件:内部维护了一个 Sync 抽象类,Sync 继承自 AQS。

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync; // 内部同步器,继承自AQS

    abstract static class Sync extends AbstractQueuedSynchronizer { ... }

    static final class NonfairSync extends Sync { ... } // 非公平锁实现
    static final class FairSync extends Sync { ... }    // 公平锁实现
    ...
}

手写简易版 ReentrantLock (MyReentrantLock.java)

import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class MyReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

    // 默认非公平锁
    public MyReentrantLock() {
        sync = new NonfairSync();
    }

    // 可选公平锁
    public MyReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    // ============  Sync 抽象类 (AQS子类)  ============
    private abstract static class Sync extends AbstractQueuedSynchronizer {
        // 尝试获取锁
        @Override
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState(); // 获取当前state值
            if (c == 0) { // state为0,表示锁未被占用
                if (compareAndSetState(0, acquires)) { // CAS尝试设置state为acquires (通常为1)
                    setExclusiveOwnerThread(current); // 设置独占锁持有线程
                    return true; // 获取锁成功
                }
            } else if (current == getExclusiveOwnerThread()) { // 重入逻辑:当前线程已持有锁
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc); // 增加重入次数
                return true; // 获取锁成功
            }
            return false; // 获取锁失败
        }

        // 尝试释放锁
        @Override
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases; // 减少state值
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            if (c == 0) { // state减为0,表示锁完全释放
                setExclusiveOwnerThread(null); // 清空独占锁持有线程
                setState(0); // 设置state为0
                return true; // 释放锁成功
            }
            setState(c); // 减少重入次数,但锁未完全释放
            return false; // 释放锁失败 (锁还被持有)
        }

        // 是否持有独占锁
        @Override
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // ConditionObject,用于支持Condition
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // 获取独占锁
        final void acquire(int acquires) {
            if (!tryAcquire(acquires) && // 尝试获取锁,失败则进入等待队列
                acquireQueued(addWaiter(Node.EXCLUSIVE), acquires)) // 加入等待队列并阻塞
                selfInterrupt();
        }

        // 释放独占锁
        final boolean release(int releases) {
            return tryRelease(releases) && tryReleaseShared(releases); // 尝试释放锁,并唤醒后继节点
        }

        // 获取Condition
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

    // ============  非公平锁实现  ============
    static final class NonfairSync extends Sync {
        @Override
        final void lock() {
            if (compareAndSetState(0, 1)) // 尝试CAS直接获取锁,不排队
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1); // CAS失败,老老实实排队去
        }
        @Override
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires); // 调用父类Sync的非公平获取逻辑
        }
    }

    // ============  公平锁实现  ============
    static final class FairSync extends Sync {
        @Override
        final void lock() {
            acquire(1); // 公平锁直接排队获取
        }

        @Override
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() && // 公平锁先检查队列是否有等待线程
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }


    // ============  Lock 接口方法实现  ============
    @Override
    public void lock() {
        sync.lock(); // 调用Sync的lock方法
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1); // 非公平尝试获取
    }

    @Override
    public boolean tryLock(long time, java.util.concurrent.TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1); // 调用Sync的release方法
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition(); // 创建Condition
    }

    // 非公平尝试获取锁 (Sync父类方法)
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) { // 再次尝试CAS,非公平体现在这里
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

代码解析

  1. Sync 抽象类:继承了 AQS,负责实现锁的核心逻辑(获取、释放、状态管理)。
  2. NonfairSync (非公平锁)
    • lock() 方法:上来先尝试 CAS 抢锁,抢不到再老实排队 (acquire)。
    • tryAcquire() 方法:复用父类 Sync 的非公平获取逻辑。
  3. FairSync (公平锁)
    • lock() 方法:直接排队 (acquire),不参与抢锁。
    • tryAcquire() 方法:先检查队列是否有等待线程 (hasQueuedPredecessors()),有则排队,没有则尝试 CAS 抢锁。
  4. Lock 接口方法lock(), unlock(), tryLock() 等方法,实际调用 Sync 类的方法。

测试代码 (MyReentrantLockTest.java)

public class MyReentrantLockTest {
    private static MyReentrantLock lock = new MyReentrantLock(true); // 使用公平锁
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                lock.lock(); // 获取锁
                try {
                    counter++; // 临界区:计数器自增
                } finally {
                    lock.unlock(); // 释放锁
                }
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println("Counter value: " + counter); // 输出最终计数器值 (应为20000)
    }
}

运行结果 (示例):

Counter value: 20000

三、AQS核心方法解读

1. tryAcquire(int acquires) (尝试获取独占锁)

  • 作用:尝试获取锁,但不阻塞。
  • 返回值:成功返回 true,失败返回 false
  • 实现要点
    • 检查同步状态 (state)。
    • CAS 修改 state。
    • 设置独占锁持有线程。

2. tryRelease(int releases) (尝试释放独占锁)

  • 作用:尝试释放锁,但不唤醒等待线程。
  • 返回值:完全释放返回 true,部分释放返回 false
  • 实现要点
    • 减少 state 值。
    • 判断是否完全释放 (state == 0)。
    • 清空独占锁持有线程。

3. acquire(int acquires) (获取独占锁,会阻塞)

  • 作用:获取锁,如果获取不到则进入等待队列阻塞。
  • 流程
    1. tryAcquire() 尝试获取锁。
    2. 失败则 addWaiter() 加入等待队列。
    3. acquireQueued() 在队列中自旋等待被唤醒。

4. release(int releases) (释放独占锁,会唤醒)

  • 作用:释放锁,并唤醒等待队列中的后继节点。
  • 流程
    1. tryRelease() 尝试释放锁。
    2. 成功则 unparkSuccessor() 唤醒后继节点。

四、面试必杀技

  1. AQS是什么?

    • 答:抽象队列同步器,是构建锁和同步器的框架,核心是 state 状态和 FIFO 等待队列。
  2. ReentrantLock 和 synchronized 的区别?

    • 答:ReentrantLock 基于 AQS,功能更强大,例如可中断、可公平锁、可绑定 Condition;synchronized 是 JVM 内置关键字,更轻量级,但功能相对简单。
  3. 公平锁和非公平锁的区别?

    • 答:公平锁严格按照 FIFO 队列排队,非公平锁允许线程“插队”抢锁,性能更高但可能导致饥饿。
  4. 手写一个简易版 ReentrantLock? (恭喜你,前面已经完成了!)


五、防翻车小贴士

  1. 理解 AQS 的核心是 状态 (state)队列 (FIFO queue)
  2. 掌握 tryAcquiretryRelease 的实现逻辑,这是自定义同步器的关键。
  3. 区分公平锁和非公平锁的实现差异。
  4. 深入理解 acquirerelease 方法的阻塞和唤醒流程。

章节总结

AQS 就像厕所排队系统,ReentrantLock 是豪华带锁厕所。 手写 ReentrantLock 就像自己组装马桶,虽然简易,但核心功能都有了。 掌握 AQS,你就能成为厕所...哦不,是并发世界的架构师!

下一章,我们继续深入并发的修罗场,聊聊线程池的那些事儿!准备好了吗? 😉