Java面试必备:深入解析AQS(AbstractQueuedSynchronizer)

Java并发面试题 - 说说AQS 吧?


什么是AQS?

AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发包中的一个核心框架,它为构建锁和同步器提供了基础实现。许多常见的并发工具如ReentrantLock、Semaphore、CountDownLatch等都是基于AQS构建的。

AQS的核心思想是:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态;如果共享资源被占用,则需要一套线程阻塞等待以及被唤醒时锁分配的机制。

AQS的核心结构

AQS内部维护了一个FIFO的双向队列(CLH队列的变种),用来存储阻塞的线程。同时使用一个volatile的int类型state变量来表示同步状态。

AbstractQueuedSynchronizer
-volatile int state
-Node head
-Node tail
+acquire(int arg)
+release(int arg)
+tryAcquire(int arg)
+tryRelease(int arg)
Node
-volatile int waitStatus
-volatile Node prev
-volatile Node next
-volatile Thread thread
-Node nextWaiter

AQS的工作原理

1. 获取资源(acquire)

成功
失败
成功
失败
尝试获取资源tryAcquire
流程结束
将当前线程包装为Node加入队列
自旋尝试获取资源
设置自己为头节点并返回
检查是否需要park当前线程
调用LockSupport.park阻塞线程

2. 释放资源(release)

失败
成功
需要唤醒后继节点
不需要唤醒
尝试释放资源tryRelease
返回false
检查头节点状态
unparkSuccessor唤醒后继节点
返回true

AQS的关键方法

AQS提供了以下关键方法供子类实现:

  1. tryAcquire(int arg) - 尝试以独占方式获取资源
  2. tryRelease(int arg) - 尝试以独占方式释放资源
  3. tryAcquireShared(int arg) - 尝试以共享方式获取资源
  4. tryReleaseShared(int arg) - 尝试以共享方式释放资源
  5. isHeldExclusively() - 判断当前线程是否独占资源

AQS的应用示例

1. 实现一个简单的互斥锁

class Mutex extends AbstractQueuedSynchronizer {
    // 尝试获取锁
    protected boolean tryAcquire(int acquires) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
    
    // 尝试释放锁
    protected boolean tryRelease(int releases) {
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
    
    // 锁是否被占用
    public boolean isLocked() {
        return getState() != 0;
    }
}

2. AQS在ReentrantLock中的应用

ReentrantLock中的公平锁和非公平锁都是基于AQS实现的:

ReentrantLock
-Sync sync
+lock()
+unlock()
«abstract»
Sync
+nonfairTryAcquire(int acquires)
+tryRelease(int releases)
NonfairSync
+tryAcquire(int acquires)
FairSync
+tryAcquire(int acquires)

AQS的两种模式

  1. 独占模式:同一时刻只有一个线程能获取资源,如ReentrantLock
  2. 共享模式:多个线程可以同时获取资源,如Semaphore、CountDownLatch

AQS的状态管理

AQS使用一个volatile的int变量state来表示同步状态,通过CAS操作来保证原子性:

  • 在ReentrantLock中,state表示锁的持有计数
  • 在Semaphore中,state表示可用许可的数量
  • 在CountDownLatch中,state表示计数器的值

AQS的等待队列

AQS的等待队列是一个CLH锁队列的变种,具有以下特点:

  1. 双向链表结构
  2. 每个节点代表一个等待线程
  3. 节点状态包括:
    • CANCELLED(1):线程已取消
    • SIGNAL(-1):后继节点需要被唤醒
    • CONDITION(-2):节点在条件队列中
    • PROPAGATE(-3):共享模式下传播
Head
Node1:ThreadA
Node2:ThreadB
Tail

AQS的公平性与非公平性

基于AQS实现的同步器可以支持公平和非公平两种模式:

  1. 公平模式:严格按照FIFO顺序获取锁
  2. 非公平模式:允许插队,可能提高吞吐量但可能导致饥饿

AQS的优缺点

优点

  1. 提供了锁和同步器的框架,减少了实现复杂度
  2. 高性能的同步机制
  3. 支持中断和超时
  4. 支持公平和非公平两种模式

缺点

  1. 理解和使用较为复杂
  2. 需要正确实现模板方法
  3. 不适合所有同步场景

总结

AQS是Java并发包的核心基础,它通过一个FIFO队列管理等待线程,使用state变量表示同步状态,并提供了可重写的模板方法供子类实现特定同步语义。理解AQS对于深入掌握Java并发编程至关重要,它不仅是许多并发工具的基础,也体现了优秀的并发设计思想。

通过合理使用AQS,我们可以构建各种高性能的同步器,满足不同的并发控制需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值