Java中的AQS(一)AQS简介

    队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架,它使用一个int成员表示同步状态,通过内部的FIFO队列来完成资源获取线程的排序工作。

一、AQS的设计

    AQS的设计是基于模板方法模式的,也就是说,使用者需要继承AQS并重写指定的方法,随后将AQS组合在自定义同步组件的实现中,并调用AQS提供的模板方法,而这些模板方法将会调用使用者重写的方法。

private volatile int state;

    AQS使用一个int的成员变量来表示同步状态。

protected final void setState(int newState) {
    state = newState;
}

    setState方法用来设置同步状态

protected final int getState() {
    return state;
}

    getState方法用来获取同步状态

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

    compareAndSetState方法使用CAS操作来讲同步状态设置为给定的值

二、AQS中的同步队列

    最开始就提到过AQS内部维护着一个FIFO的队列。而AQS就是依赖这个同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点Node,并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。

    同步队列中节点属性:

//共享锁对应的节点
static final Node SHARED = new Node();

    因为如果是共享锁,线程可以被多个线程获得。所以将这个属性定义为一个常量。

//独占锁对应的节点
static final Node EXCLUSIVE = null;

    独占锁因为只能对一个线程获得,所以设置为null,当某个线程获得锁时,将该线程对应的赋予这个属性

//节点的等待状态
volatile int waitStatus;

    节点的等待状态有4个

        (1)CANCELLED:值为1,由于在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消等待,节点进入该状态将不会变化

        (2)SINGAL:值为-1,后继节点的线程处于等待状态,当前节点如果释放了同步状态,将会通知后继节点,使后继节点得以运行

        (3)CONDITION:值为-2,节点在等待队列中(这个在Condition的博客里会讲到),节点线程等待在Condition上,当其他线程对Condition调用了singal后,该节点会从等待队列转移到同步队列,加入到对同步状态的获取中去

        (4)PROPAGEATE:值为-3,表示下一次共享式同步状态获取将会无条件被传播下去

//前驱节点,当节点加入同步队列时被设置(尾部添加)
volatile Node prev;

    同步队列中某个节点的前驱节点

//后继节点
volatile Node next;

    同步队列中某个节点的后继节点

//等待队列的后继节点。如果当前节点是共享的,那么这个字段将是一个SHARED常量
Node nextWaiter;

    这个是等待队列的后继节点(不是同步队列)

//获取同步状态的线程
volatile Thread thread;

    当前获取到同步状态的线程

节点时构成同步队列的基础,AQS拥有首节点和尾节点,没有成功获取到同步状态的节点会加入到同步队列的尾部,同步队列的结构如下图所示


同步器AQS包含两个节点类型的引用,一个指向头结点,一个指向尾节点。

三、同步队列的操作

    1、将节点加入到同步队列:当一个线程成功获取了同步状态(或者锁),其他线程将无法获取到同步状态,转而被构造成节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全。AQS提供了一个基于CAS的设置尾节点的方法:compareAndSerTail,它需要传递当前线程认为的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。


    2、将节点设置为首节点:同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点。


    设置首节点是通过成功获取同步状态的线程完成的,由于只有一个线程能成功获取到同步状态,因此设置头节点并不需要使用CAS来保证,它只需要将首节点设置为原首节点的后继节点并断开原首节点的next引用即可。




猜你喜欢

转载自blog.csdn.net/yanghan1222/article/details/80247844
AQS