当前位置: 面试刷题>> 说说 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实现的独占锁(简化版)的伪代码框架: ```java // 假设的基于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的`tryAcquire`和`tryRelease`等方法,开发者可以构建出各种复杂的同步组件,如锁、信号量等。理解AQS的工作原理对于深入掌握Java并发编程至关重要。在实际项目中,合理利用AQS及其派生类可以大大提升并发程序的性能和可靠性。在探索更多并发编程技巧时,不妨关注“码小课”网站,那里有更深入、更系统的学习资源和实战案例。
推荐面试题