AQS的底层实现原理

AQS就是使用一个state值,和FIFO队列来实现多线程下对共享资源的访问。并且state值是被volatile修饰。主要实现有独占模式和共享模式。在独占模式下,如果线程想要获取锁需要通过cas的方式修改state值为1,并设置持有锁的线程为当前线程,释放锁时需要判断是否是当前线程持有锁,如果不是throw new IllegalMonitorStateException();如果是通过setExclusiveOwnerThread(null)释放锁,并通过cas的方式将state设置为0。如果线程获取锁失败会将当前线程加入到CLH队列,采用双向链表结构,节点包含线程的引用、等待状态(status>0表示需要被唤醒,为0表示不需要)以及前驱和后继节点的职责。对于获取锁失败的线程会通过循环不断检查节点状态,等待其他线程释放锁,然后去获取锁。 CLH双向链表

  • 获取锁失败的线程会被封装成 Node 并加入队列。

  • 队列采用 FIFO 策略,但支持条件变量(Condition)唤醒。

    public final void acquire(int arg) { if (!tryAcquire(arg)) acquire(null, arg, false, false, false, 0L); //通过无限循环 (for(;;)) 持续检查当前节点的状态,包括是否为队列头节点、前驱节点状态等。尝试去获取锁。 }

java 复制代码
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        signalNext(head);//释放成功后唤醒下一节点
        return true;
    }
    return false;
}
private static void signalNext(Node h) {
    Node s;
    if (h != null && (s = h.next) != null && s.status != 0) {//判断后继节点是否存在,以及它的状态,确保后继节点 s 的状态 status 不为 0(表示需要被唤醒)。
        s.getAndUnsetStatus(WAITING); //取消设置后继节点状态为waiting
        LockSupport.unpark(s.waiter); //唤醒后继结点的关联线程,调用此方法会取消对指定线程的阻塞状态,使其有机会重新获得CPU执行权。
    }
}

AQS还有一个内部类,ConditionObject,主要用于实现线程的等待/通知机制。

csharp 复制代码
abstract static class Node { //clh队列
    volatile Node prev;       // initially attached via casTail
    volatile Node next;       // visibly nonnull when signallable
    Thread waiter;            // visibly nonnull when enqueued
    volatile int status;      // written by owner, atomic bit ops by others
}
arduino 复制代码
/**字段偏移量计算:利用 Unsafe.objectFieldOffset 计算 AbstractQueuedSynchronizer 类中
*/state、head 和 tail 字段的内存偏移量,便于后续通过 CAS 操作高效更新这些字段。
private static final Unsafe U = Unsafe.getUnsafe();
private static final long STATE
    = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "state");
private static final long HEAD
    = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "head");
private static final long TAIL
    = U.objectFieldOffset(AbstractQueuedSynchronizer.class, "tail");

AQS(AbstractQueuedSynchronizer)是 Java 并发包 (java.util.concurrent.locks) 的核心同步框架,许多同步工具(如 ReentrantLockSemaphoreCountDownLatch 等)都是基于它实现的。

AQS 的核心思想是:

  • 管理一个共享的同步状态(state ,并提供 CAS(Compare-And-Swap) 操作来修改状态。
  • 维护一个 CLH 变体的 FIFO 线程等待队列,用于管理获取锁失败的线程。
  • 支持独占模式(如 ReentrantLock)和共享模式(如 Semaphore
java 复制代码
//独占锁示例
class Mutex implements Lock, java.io.Serializable {
    // Our internal helper class
    private static class Sync extends AbstractQueuedSynchronizer {
        // Acquires the lock if state is zero
        public boolean tryAcquire(int acquires) {
            assert acquires == 1; // Otherwise unused
            if (compareAndSetState(0, 1)) { //比较并交换
                setExclusiveOwnerThread(Thread.currentThread()); //设置当前拥有独占访问权限的线程。
                return true;
            }
            return false;
        }
        // Releases the lock by setting state to zero
        protected boolean tryRelease(int releases) {
            assert releases == 1; // Otherwise unused
            if (!isHeldExclusively()) //判断要释放锁的线程是否是加锁线程
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);//释放锁,参数 null 表示没有线程拥有访问权限。
            setState(0); //设置state值为0
            return true;
        }
        // Reports whether in locked state
        public boolean isLocked() {
            return getState() != 0;
        }
        public boolean isHeldExclusively() {  //判断要释放锁的线程是否是加锁线程
            // a data race, but safe due to out-of-thin-air guarantees
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        // Provides a Condition
        public Condition newCondition() {  
            return new ConditionObject();
        }
        // Deserializes properly
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    // The sync object does all the hard work. We just forward to it.
    private final Sync sync = new Sync();
    public void lock()              { sync.acquire(1); } //父类的
    public boolean tryLock()        { return sync.tryAcquire(1); }//子类的
    public void unlock()            { sync.release(1); } //父类
    public Condition newCondition() { return sync.newCondition(); } //子类
    public boolean isLocked()       { return sync.isLocked(); } 
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
    public boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
}
相关推荐
.生产的驴21 分钟前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
猿周LV28 分钟前
JMeter 安装及使用 [软件测试工具]
java·测试工具·jmeter·单元测试·压力测试
晨集30 分钟前
Uni-App 多端电子合同开源项目介绍
java·spring boot·uni-app·电子合同
时间之城33 分钟前
笔记:记一次使用EasyExcel重写convertToExcelData方法无法读取@ExcelDictFormat注解的问题(已解决)
java·spring boot·笔记·spring·excel
椰羊~王小美40 分钟前
LeetCode -- Flora -- edit 2025-04-25
java·开发语言
凯酱1 小时前
MyBatis-Plus分页插件的使用
java·tomcat·mybatis
程序员总部1 小时前
如何在IDEA中高效使用Test注解进行单元测试?
java·单元测试·intellij-idea
oioihoii1 小时前
C++23中if consteval / if not consteval (P1938R3) 详解
java·数据库·c++23
佳腾_1 小时前
【Web应用服务器_Tomcat】一、Tomcat基础与核心功能详解
java·前端·中间件·tomcat·web应用服务器
异常君2 小时前
线程池隐患解析:为何阿里巴巴拒绝 Executors
java·后端·代码规范