AQS——同步器框架之源

1. AQS原理

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

1.1 AQS的继承关系

ReentrantLockSemaphore之间的继承体系

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!

相关推荐
Victor356几秒前
MySQL(162)MySQL中的行级锁和表级锁有什么区别?
后端
周末程序猿17 分钟前
技术总结|如何使用提升 strlen 的性能?
后端·算法
tang_jian_dong40 分钟前
springboot + vue3 拉取海康视频点位及播放
spring boot·后端·音视频
程序员爱钓鱼1 小时前
Go语言实战案例-括号匹配算法
后端·google·go
程序员爱钓鱼1 小时前
Go语言实战案例-判断字符串是否由另一个字符串的字母组成
后端·google·go
郝学胜-神的一滴3 小时前
SpringBoot实战指南:从快速入门到生产级部署(2025最新版)
java·spring boot·后端·程序人生
爷_8 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
不过普通话一乙不改名11 小时前
第一章:Go语言基础入门之函数
开发语言·后端·golang
豌豆花下猫12 小时前
Python 潮流周刊#112:欢迎 AI 时代的编程新人
后端·python·ai
Electrolux12 小时前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法