AQS——同步器框架之源

1. AQS原理

AQS是AbstractQueuedSynchronizer的简称。AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架。

1.1 AQS的继承关系

与ReentrantLock和Semaphore之间的继承体系

1.2 AQS的实现原理

AQS中 维护了一个整型变量 state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。

  • state 初始化 0,在多线程条件下,线程要执行临界区的代码,必须首先获取 state,某个线程获取成功之后, state 加 1,其他线程再获取的话由于共享资源已被占用,所以会到 FIFO 等待队列去等待,等占有 state 的线程执行完临界区的代码释放资源( state 减 1)后,会唤醒 FIFO 中的下一个等待线程(head 中的下一个结点)去获取 state。

  • state 由于是多线程共享变量,所以必须定义成 volatile,以保证 state 的可见性, 同时虽然 volatile 能保证可见性,但不能保证原子性,所以 AQS 提供了对 state 的原子操作方法,保证了线程安全。

  • 另外 AQS 中实现的 FIFO 队列(CLH 队列)其实是双向链表实现的,由 head, tail 节点表示,head 结点代表当前占用的线程,其他节点由于暂时获取不到锁所以依次排队等待锁释放。

java 复制代码
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizerimplements java.io.Serializable {
    // 以下为双向链表的首尾结点,代表入口等待队列
    private transient volatile Node head;
    private transient volatile Node tail;
    // 共享变量 state
    private volatile int state;
    // cas 获取 / 释放 state,保证线程安全地获取锁
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this\
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
 }

2. 源码解析

通过对ReentrantLock独占锁源码的分析,来理解AQS的底层原理,共享锁与 Condition 的实现与独占锁和非公平锁原理相似,在此不过多阐述。

java 复制代码
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
java 复制代码
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    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;
    }
}
java 复制代码
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

相关链接

我画了35张图就是为了让你深入 AQS
1.5w字,30图带你彻底掌握 AQS!

相关推荐
努力的小郑38 分钟前
数据查到了,回答去哪儿了?一次百炼 + LangChain 排查实录
后端·langchain·agent
isfox1 小时前
Python 虚拟环境:别再把所有项目塞进一个"大仓库"了
后端
易师傅2 小时前
技术面试已死?AI 时代,真正拉开程序员差距的是什么?
前端·后端·程序员
苍何2 小时前
办公 Agent 权威评测榜单出炉了,豆包第一
后端
AiMan2 小时前
别再把秘塔会话逐个点开:这可把秘塔多个会话一次导出?AI导出鸭
前端·后端·面试
nice_lcj5203 小时前
【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端
java·后端·spring·spring ai·ai应用开发
卷无止境3 小时前
mathmodel-figure-templates 以及两个上手案例
后端·python
Moment3 小时前
写给迷茫的应届生,怎样在 AI 时代争取更多机会
前端·后端·面试
回家路上绕了弯3 小时前
为什么还是不能直接把需求丢给 AI?
后端
锋行天下4 小时前
服务器 Python 2.7 装不了 langgraph-cli?用本地生成 Dockerfile 曲线救国
人工智能·后端·python