队列同步器 AQS

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DT_Zhangshuo/article/details/82946770

队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架,首先要知道这个共享变量的状态(是否已经被其他线程锁住等),这设置了一个int成员变量表示同步状态,通过内置的先进先出队列来完成资源获取线程的排队工作。一些线程无法获取到共享资源等待获取锁而进入一个容器中进行保存,而这容器就是一个先进先出队列。

同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态(上文的int成员变量)进行修改,这时就需要使用同步器提供的3个方法(getState()、setState()、compareAndSetState()) 来进行操作,因为它们能够保证状态的改变是安全的。

下面是内部类Node源码:

static final class Node {
        /** 标识一个这个节点是否为shared类型 */
        static final Node SHARED = new Node();
        /** 表示节点在独占模式下等待的标记 */
        static final Node EXCLUSIVE = null;
 
        /** 等待状态值表明线程已经取消*/
        static final int CANCELLED =  1;
        /** 等待状态值来指示继任者的线程需要取消等待 */
        static final int SIGNAL    = -1;
        /** 等待状态值表明线程正在等待条件*/
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;
 
        /**
         *   等待状态值,又以下状态值:
		 *
         *   SIGNAL:	 值为-1 ,后续节点处于等待状态,而当前节点的线程如果
		 * 			 	 释放了同步状态或者取消等待,节点进入该状态不会变化          
         *   CANCELLED:  值为 1,由于在同步队列中等待的线程等待超时或者被中断
         *               需要从同步队列中取消等待,节点进入该状态将不会变化                    
         *   CONDITION:  值为-2,节点在等待队列中,节点线程等待在Condition上,
         *               当其他线程对Condition调用了signal()方法后,该节点将会
         *               从等待队里中转移到同步队列中,加入对同步状态的获取中
        
         *   PROPAGATE:  值为-3,表示下一次共享式同步状态获取将会无条件地被传播下去
         *            
         *   0:          初始化状态
         */
        volatile int waitStatus;
 
        /**
		 * 前驱节点,当节点加入同步队列时被设置
         */
        volatile Node prev;
 
         /**
		 * 后继节点
         */
        volatile Node next;
 
        /**
		 * 获取状态状态的线程
         */
        volatile Thread thread;
 
         /**
		 * 等待队列中的后继节点。如果当前节点是共享的,那么这个字段是一个shared常量,
		 * 也就是说节点类型(独占或共享)和等待队列中个后继节点共用同一个字段
		 */
        Node nextWaiter;
 
        /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
 
        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        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;
        }
    }

子类推荐被定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放方法来供自定义同步组件使用,同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组(ReentranLock、ReentranReadWriteLock、CountDownLatch等)。
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者的关系:

(1) 锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个 线程并行访问),隐藏了实现细节。
(2) 同步器面向的是锁的使用者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。或者可以把AQS认为是锁的实现者的一个父类。

AbstractQueuedSynchronizer的主要方法:
大致将AbstractQueuedSynchronizer的方法分为以下几种:
(1) Public final
(2) Protected final
(3) Private
(4) Protected

AbstractQueuedSynchronzier是一个抽象类,但是却没有一个抽象方法,但是主要的方法可分为上述的四种,我们知道final修饰的方法是不可以被子类重写的,protected修饰的方法是可以被子类重载的,下边展示一下大致分的四类方法。
1: Protected 类别
AbstractQueuedSynchornizer虽然没有抽象方法,但是提供了五个方法可以让我们在子类中重载,并且这五个方法都是空的实现且直接抛出异常,也就是说我们要使用这五个方法所提供的功能,必须在子类中自己实现,这也是模板方法模式的一种体现和使用。这五个方法的具体含义如下:
(1) Boolean tryAcquire(int arg):独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态
(2) Boolean tryRelease(int arg):独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态
(3) Int tryAcquireShared(int arg):共享式获取同步状态,返回大于等于0的值,表示获取成功,反之获取失败
(4) Boolean tryReleaseShared(int arg) 共享式释放同步状态
(5) Boolean is HeldExclusively():当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程锁独占。

2:Public final 类别

除了上述protected的方法之外,还有一个关键的类别就是public final,这是我们可以直接使用的方法,称之为模板方法,当我们实现自定义的同步组件的时候,我们可以调用这些模板方法获取我们需要的东西。
(1) Void acquire(int arg) 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将进入同步队列等待,该方法将会调用重写的tryAcquire(int arg) 方法
(2) Void acquireInterruptibly(int arg) 与acquire相同,但是该方法会响应中断,当前线程未获取到同步状态而进入同步队列中,如果当前线程被中断,则该方法会抛出InteruptedException并返回
(3) Boolean tryAcquireNanos(int arg,long nanos) 在acquireInterruptibly(int arg )的基础上增加了超时限制 如果当前线程在超时时间内没有获取到同步状态,那么将会返回false,如果获取到了返回为true
(4) Void acquireShared(int arg) 共享式的获取同步状态,如果当前线程为获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态。
(5) Void acquireSharedInterruptibly(int args) 与acquireShared(int arg)相同,该方法响应中断
(6) boolean tryAcquireSharedNanos(int arg,long nanos) 在acquireShared(int arg) 基础上增加了超时限制
(7) boolean release(int arg) 独占式的释放同步状态,该方法会在释放同步状态之后,将同步队列
(8) boolean releaseShared(int arg) 共享式的释放同步状态
(9) Collection< Thread >getQueuedThreads() 获取等待在同步队列上的线程集合。

同步器提供的上述模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放同步状态、查询同步队列中的等待线程情况。
自定义同步组件将使用同步器提供的模板方法来实现自己的同步语义。只有掌握了同步器的工作原理才能更加深入地理解并发包中的其他组件。

3:protected final 类别
上文中,我们至少知道了我们要对int类型的同步状态进行修改,下边的三个方法提供了可以修改:
getState():int 获取当前同步状态
setState(int):void 设置当前同步状态
compareAndSetState(int,int):boolean 使用CAS设置当前状态,该方法能保证状态设置的原子性。
另外还有三个:hasWaiters、getWaitQueueLength、getWaitingThreads三个方法
再看一下AbstractQueuedSynchronizer的内部类:
AbstractQueuedSynchornizer 有两个内部类:一个是ConditionObject,另一个是Node。

ConditionObject内部类:
(1) ConditionObjecy
这个我们知道在使用synchronized的时候是使用wait和notify进行线程间通信,使用ReentranLock的时候是使用Condition实现的线程通信,而这正是AbstractQueuedSynchronizer帮我们进一步封装的Condition接口:
(2) Condition接口如下:
(3) ConditionObject实现了Condition接口:
(4) 调用ReentranLock的newCondition方法正是返回的是ConditionObject对象

猜你喜欢

转载自blog.csdn.net/DT_Zhangshuo/article/details/82946770