AQS原理解析及源码分析

目录

1、介绍下AQS几个重要的组件

2、内部成员变量state

3.   同步队列NODE

4、等待队列 condition


 


AbstractQueuedSynchronizer又称为队列同步器(后面简称AQS)。

AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。

AQS是一个抽象类。它是用来构建锁或其他同步组件的基础框架。查看继承关系

由上图可以看出。ReentrantLock、ReentrantReadWriteLock、Semaphore、以及线程池的工作线程也用的AQS的实现。

AQS是一个典型的是利用模板方法设计模式设计的一个工具。利用抽象类将公共实现的部分在抽象类的实现。具体的获取锁,释放锁的操作由各子类去实现、例如ReentrantLock和Semaphore。

1、介绍下AQS几个重要的组件

   1.1 AQS内部成员变量state. 利用CAS原子操作,操作该变量,当state=0的时候 即没有线程去竞争共享资源,如果state>=1(支持锁可重入性 所以state>=1)说明共享资源已经被占用。

   1.2 同步队列,同一个时间只会有一个线程去操作共享资源,其他未竞争到的线程就进入维护到一个同步队列,该队列由NODE节点维护,在后面我在详细的介绍

   1.3 等待队列 condition 如果上述的同步队列的线程是等待的(有序或者无序即公平锁和非公平锁的实现)去获取锁,那么等待队列便是通过await和single等待和唤醒功能去加入到同步队列去获取锁。如果一直在等待队列不被唤醒。那么一直不会获取到锁之前。

 

2、内部成员变量state

所有的线程都去获取和释放state的值达到资源锁的效果


    /**
     * The synchronization state.
     */
   // volatile 保证所有的线程可见 
   private volatile int state;

     
    //获取内存中最新的是state值
    protected final int getState() {
        return state;
    }

    //set state的值
    protected final void setState(int newState) {
        state = newState;
    }

    // 通过原子操作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);
    }

3.   同步队列NODE

同步队列NODE是AQS的一个内部类

static final class Node {

CyclicBarrier

    /** 标记是否共享模式 */
    static final Node SHARED = new Node();
    /** 标记是否独占模式 */
    static final Node EXCLUSIVE = null;

     //waitStatus 节点的状态值
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    
      
    volatile int waitStatus;

    // 该节点的前置节点
    volatile Node prev;

    // 该节点的后置节点
    volatile Node next;

   //线程信息
    volatile Thread thread;

    //下一个等待节点
    Node nextWaiter;

 
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    //返回当前节点的前置节点
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

模式

1.Exclusive:独占,只有一个线程能执行,如ReentrantLock
2.Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,

waitStatus:

    1.CANCELLED 
     由于超时或中断,该节点被取消。
     节点永远不会离开这个状态。特别是,
     取消节点的线程不再阻塞。
     2.SIGNAL 等待触发状态,前节点可能是head或者前节点为取消状态CANCELLED
     3.CONDITION  与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

     4.PROPAGATE 与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态。

 

4、等待队列 condition

ConditionObject 是AQS的内部类 ConditionObject就是实现的condition接口

需要注意的是。只有获取到锁的线程才可能是condition状态 另外只有在独占模式下才会有condition 相当于线程通信工具 与隐形锁syn的wait和notify是一个意思、

调用await需要去释放当前锁。

    public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
        public ConditionObject() { }

       

           
        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

        /**
         * Removes and transfers nodes until hit non-cancelled one or
         * null. Split out from signal in part to encourage compilers
         * to inline the case of no waiters.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

        /**
         * Removes and transfers all nodes.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }

        /**
         * Unlinks cancelled waiter nodes from condition queue.
         * Called only while holding lock. This is called when
         * cancellation occurred during condition wait, and upon
         * insertion of a new waiter when lastWaiter is seen to have
         * been cancelled. This method is needed to avoid garbage
         * retention in the absence of signals. So even though it may
         * require a full traversal, it comes into play only when
         * timeouts or cancellations occur in the absence of
         * signals. It traverses all nodes rather than stopping at a
         * particular target to unlink all pointers to garbage nodes
         * without requiring many re-traversals during cancellation
         * storms.
         */
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }

        // public methods

        /**
         * Moves the longest-waiting thread, if one exists, from the
         * wait queue for this condition to the wait queue for the
         * owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

        /**
         * Moves all threads from the wait queue for this condition to
         * the wait queue for the owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }

        /**
         * Implements uninterruptible condition wait.
         * <ol>
         * <li> Save lock state returned by {@link #getState}.
         * <li> Invoke {@link #release} with saved state as argument,
         *      throwing IllegalMonitorStateException if it fails.
         * <li> Block until signalled.
         * <li> Reacquire by invoking specialized version of
         *      {@link #acquire} with saved state as argument.
         * </ol>
         */
        public final void awaitUninterruptibly() {
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            boolean interrupted = false;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if (Thread.interrupted())
                    interrupted = true;
            }
            if (acquireQueued(node, savedState) || interrupted)
                selfInterrupt();
        }
      

      public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            long savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

 

 

参考博客:

https://blog.csdn.net/javazejian/article/details/75043422

https://blog.csdn.net/mulinsen77/article/details/84583716

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值