当前位置: 面试刷题>> 说说 AQS 吧?


在深入探讨AQS(AbstractQueuedSynchronizer)这一Java并发包中的核心同步器之前,我们需要先理解它在Java并发编程中的位置和作用。AQS作为java.util.concurrent.locks包的基础框架,为构建锁(如ReentrantLock)和其他同步类(如CountDownLatch、Semaphore等)提供了一种高度灵活且强大的机制。它通过一个共享的int状态变量和一个FIFO(先进先出)的等待队列来管理对共享资源的访问。

AQS的核心概念

  1. 状态变量(State):AQS内部维护了一个volatile int类型的状态变量,用于表示同步状态。根据具体实现,这个状态可以表示锁被持有的次数(如ReentrantLock)、信号量的可用数量(如Semaphore)等。

  2. 等待队列(Wait Queue):AQS内部通过一个CLH(Craig, Landin, and Hagersten)队列变体来实现等待队列,用于管理那些未能成功获取同步状态的线程。这个队列是线程安全的,并且以FIFO的方式管理线程,确保了公平性(尽管AQS也支持非公平模式)。

AQS的工作流程

AQS的工作流程主要分为两个核心操作:acquire(获取)和release(释放)同步状态。

  • acquire(获取):当线程尝试获取同步状态时,首先会尝试以非阻塞方式更新状态值。如果失败,则会将当前线程和等待状态(如独占或共享)封装成一个节点(Node),并将其加入到等待队列的尾部。之后,线程会进入一个自旋循环(Spin Loop),在这个循环中尝试以阻塞或非阻塞方式获取同步状态,直到成功为止。

  • release(释放):当线程成功完成同步代码块的执行后,会调用release方法来释放同步状态。释放过程会尝试更新状态值,如果更新成功,则会唤醒等待队列中的第一个线程(或某些线程,取决于同步器的实现),使其有机会尝试获取同步状态。

示例代码(简化版)

虽然直接展示AQS的内部实现代码可能过于复杂,但我们可以通过一个基于AQS的简单锁实现来感受其工作原理。以下是一个基于AQS实现的独占锁(简化版)的伪代码框架:

// 假设的基于AQS的独占锁实现
class MyLock extends AbstractQueuedSynchronizer {

    // 尝试获取锁
    public void lock() {
        acquire(1); // 尝试将状态设置为1(锁定)
    }

    // 尝试释放锁
    public boolean unlock() {
        return release(1); // 尝试将状态减少1(解锁)
    }

    // 是否由当前线程持有锁
    protected boolean isHeldExclusively() {
        // 当且仅当状态为1且当前线程是独占线程时返回true
        return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();
    }

    // 尝试获取锁(自定义逻辑,通常不需要重写)
    protected boolean tryAcquire(int acquires) {
        assert acquires == 1; // 否则未定义
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    // 尝试释放锁(自定义逻辑,通常不需要重写)
    protected boolean tryRelease(int releases) {
        assert releases == 1; // 否则未定义
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
}

总结

AQS是Java并发包中的一个基础且强大的同步器,它通过状态变量和等待队列管理对共享资源的访问。通过实现AQS的tryAcquiretryRelease等方法,开发者可以构建出各种复杂的同步组件,如锁、信号量等。理解AQS的工作原理对于深入掌握Java并发编程至关重要。在实际项目中,合理利用AQS及其派生类可以大大提升并发程序的性能和可靠性。在探索更多并发编程技巧时,不妨关注“码小课”网站,那里有更深入、更系统的学习资源和实战案例。