AQS框架源码分析

AQS就是AbstractQueuedSynchronizer,它是为实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器(信号量,事件等)提供的一个框架。AQS继承了AbstractOwnableSynchronizer类,这个类为创建锁和相关同步器提供了基础。AQS是Concurrent包的核心,lock就是在AQS的基础上实现的,阻塞队列,线程池,信号量等都离不开AQS的支持。

CLH

CLH lock queue通常被用来处理并发的情况,它通过双向队列(FIFO)来完成同步状态。每个线程都会被封装成一个Node节点放到同步队列中。

每个Node节点保存了当前线程的同步状态,等待状态,前驱和后继节点等。

static final class Node {
        // 共享模式下等待的标记
        static final Node SHARED = new Node();
        // 独占模式下等待的标记
        static final Node EXCLUSIVE = null;

        // 线程的等待状态 表示线程已经被取消
        static final int CANCELLED =  1;
        // 线程的等待状态 表示后继线程需要被唤醒
        static final int SIGNAL    = -1;
        // 线程的等待状态 表示线程在Condtion上
        static final int CONDITION = -2;
        
        // 表示下一个acquireShared需要无条件的传播
        static final int PROPAGATE = -3;

        /**
         *   SIGNAL:     当前节点的后继节点处于等待状态时,如果当前节点的同步状态被释放或者取消,
         *               必须唤起它的后继节点
         *         
         *   CANCELLED:  一个节点由于超时或者中断需要在CLH队列中取消等待状态,被取消的节点不会再次等待
         *               
         *   CONDITION:  当前节点在等待队列中,只有当节点的状态设为0的时候该节点才会被转移到同步队列
         *               
         *   PROPAGATE:  下一次的共享模式同步状态的获取将会无条件的传播
 
         * waitStatus的初始值时0,使用CAS来修改节点的状态
         */
        volatile int waitStatus;

        /**
         * 当前节点的前驱节点,当前线程依赖它来检查waitStatus,在入队的时候才被分配,
         * 并且只在出队的时候才被取消(为了GC),头节点永远不会被取消,一个节点成为头节点
         * 仅仅是成功获取到锁的结果,一个被取消的线程永远也不会获取到锁,线程只取消自身,
         * 而不涉及其他节点
         */
        volatile Node prev;

        /**
         * 当前节点的后继节点,当前线程释放的才被唤起,在入队时分配,在绕过被取消的前驱节点
         * 时调整,在出队列的时候取消(为了GC)
         * 如果一个节点的next为空,我们可以从尾部扫描它的prev,双重检查
         * 被取消节点的next设置为指向节点本身而不是null,为了isOnSyncQueue更容易操作
         */
        volatile Node next;

        /**
         * 当前节点的线程,初始化后使用,在使用后失效 
         */
        volatile Thread thread;

        /**
         * 链接到下一个节点的等待条件,或特殊的值SHARED,因为条件队列只有在独占模式时才能被访问,
         * 所以我们只需要一个简单的连接队列在等待的时候保存节点,然后把它们转移到队列中重新获取
         * 因为条件只能是独占性的,我们通过使用特殊的值来表示共享模式
         */
        Node nextWaiter;

        /**
         * 如果节点处于共享模式下等待直接返回true
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回当前节点的前驱节点,如果为空,直接抛出空指针异常
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // 用来建立初始化的head 或 SHARED的标记
        }

        Node(Thread thread, Node mode) {     // 指定线程和模式的构造方法
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // 指定线程和节点状态的构造方法
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

AQS具有头节点和尾节点

private transient volatile Node head;
private transient volatile Node tail;

Node是构成同步队列的基础,看一下Node的结构


同步队列中首节点是获取到锁的节点,它在释放的时候会唤醒后继节点,后继节点获取到锁的时候,会把自己设为首节点。


注意,设置首节点不需要使用CAS,因为在并发环境中只有一个线程都获取到锁,只有获取到锁的线程才能设置首节点。

独占式

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

通过调用acquire的方法获取同步状态,该方法忽略中断,线程获取同步状态失败后,进入同步队列,在对其进行中断操作后,线程不会从同步队列移除。首先调用tryAcquire方法获取同步状态,AQS并没有实现这个方法,具体的实现由它的继承类进行重写,比如ReentrantLock的Sync类。如果获取同步状态成功的直接返回true;如果获取同步状态失败的话,调用addWaiter方法把线程封装成一个Node节点添加到同步队列的尾部,最后调用acquireQueued方法使节点以自旋的方式获取同步状态,如果获取同步状态失败,要挂起线程,最后,线

  • 19
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java中的AQS(AbstractQueuedSynchronizer)是实现锁和同步器的一种重要工具。在AQS中,一个节点表示一个线程,依次排列在一个双向队列中,同时使用CAS原子操作来保证线程安全。当多个线程对于同一资源竞争时,一个节点会被放置在队列的尾部,其他线程则在其之前等待,直到该资源可以被锁定。 当一个线程调用lock()方法进行锁定时,它会首先调用tryAcquire()方法尝试获取锁。如果当前资源尚未被锁定,则该线程成功获取锁,tryAcquire()返回true。如果当前资源已被锁定,则线程无法获取锁,tryAcquire()返回false。此时该线程就会被加入到等待队列中,同时被加入到前一个节点的后置节点中,即成为它的后继。然后该线程会在park()方法处等待,直到前一个节点释放了锁,再重新尝试获取锁。 在AQS中,当一个节点即将释放锁时,它会调用tryRelease()方法来释放锁,并唤醒后置节点以重试获取锁。如果当前节点没有后置节点,则不会发生任何操作。当一个线程在队列头部成功获取锁和资源时,该线程需要使用release()方法释放锁和资源,并唤醒等待队列中的后置节点。 总之,AQS中的锁机制是通过双向等待队列实现的,其中节点表示线程,使用CAS原子操作保证线程安全,并在tryAcquire()和tryRelease()方法中进行锁定和释放。该机制保证了多线程环境下资源的正确访问和线程的安全执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值