多线程之AbstractQueuedSynchronizer(AQS)

AQS 是 java.util.concurrent.locks 包下一个抽象的队列式同步器类 ;

一、简介:

为JUC(java.util.concurrent)包下的很多锁和同步器提供了基础(通俗的讲把 JUC下依赖与AQS 的同步器和锁比作车,AQS 就是车的动力系统,具体有哪些车的动力系统依赖AQS呢 ?别急慢慢往下看);

先简单看完 javadoc 文档:

大概能定位到几个重点:

1.子类必须定义改变此状态的受保护的方法: 子类必须定义改变次状态的受保护方法,aqs 内部定义了单个原子int值 (state)来表示状态,int状态的改变在子类必须受保护,否则会破坏AQS中的同步机制,这是硬性要求'必须'

2. 根据该对象被获取或者释放来定义该状态的含义;举个例子 :

            ReentrantLock 用 state 来表示锁的重入次数,每次 lock(); state + 1。unlock(); state - 1;

            CountDownLatch 用 state 表示总数,每次 countDown();  state值 -1;

3. 独占模式 共享模式:

独占模式:多条线程同一时间操作,只能允许一条线程成功;方法有(acquire 独占获取、release 独占发布、tryAcquire 独占尝试获取)ReentrantReadWriteLock的写采用独占模式;

共享模式:多条线程同一时间操作,当一条成功时接下来的全部成功; 方法有(acquireShared 共享获取、releaseShared 共享发布、 tryAcquireShared 共享尝试获取)ReentrantReadWriteLock的读采用共享模式;

二、实现:

首先AQS内部维护着一个用来记录等待的链表,链表如下:

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;
    //表示线程正在等待某个条件
    static final int CONDITION = -2;
    //表示下一个共享模式的节点应该无条件的传播下去
    static final int PROPAGATE = -3;

    //状态位 ,分别可以使CANCELLED、SINGNAL、CONDITION、PROPAGATE、0 
    volatile int waitStatus;

    volatile Node prev;//前驱节点
    volatile Node next;//后继节点
    volatile Thread thread;//等待锁的线程

    //ConditionObject链表的后继节点或者代表共享模式的节点。
    //因为Condition队列只能在独占模式下被能被访问,我们只需要简单的使用链表队列来链接正在等待条件的节点。
    //然后它们会被转移到同步队列(AQS队列)再次重新获取。
    //由于条件队列只能在独占模式下使用,所以我们要表示共享模式的节点的话只要使用特殊值SHARED来标明即可。
    Node nextWaiter;
    //Returns true if node is waiting in shared mode
    final boolean isShared() {
            return nextWaiter == SHARED;
    }
    .......
 }

AQS中维护的变量 

// transient 关键字不参与序列化
// volatile 关键字可见的 能保证被修饰元素的可见性
// head 链表的头部(第一个)元素
private transient volatile Node head;
//tail 链表的尾部 (最后一个)元素
private transient volatile Node tail;
// 上面讲过 用来表示状态
private volatile int state;

    AQS 中 Node 结点的操作都是基于 CAS (compare and swap) 来实现, 所以可以保证原子性;

AQS中维护的一个内部类 ConditionObject 主要实现 Condition ,若不了解大家可以自行查阅资源(Condition.await() 类似于 Object.wait();  Condition.signal()类似于 Object.notify() ; Condition.signalAll()类似于 Object.notifyAll())

public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        private transient Node firstWaiter;    //队列头的等待者
        private transient Node lastWaiter;    //队列尾的等待者
        public ConditionObject() { }
        //将当前线程添加条件等待,放在等待队列的尾部
        private Node addConditionWaiter() {
            Node t = lastWaiter;
            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;
        }

...

}

 

以 acquire 以及 release 为例 简单阐述代码:

这是 acquire 方法

//该方法是 final 修饰的不可重写的方法 提供一个模板方法:子类只需重写 tryAcquire 方法即可
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        //首先 addWaiter 为当前线程和给定模式创建并对节点进行排队(这是独占模式)
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // acquireQueued 自旋阻塞
        selfInterrupt(); // 停止线程 实现就是 Thread.currentThread().interrupt();
}
//尝试获取一下资源 需要子类重写 
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
//为当前线程和给定模式创建并对节点进行排队
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);  // new 一个 Node
    Node pred = tail;  // 获取尾部结点
    if (pred != null) {
        node.prev = pred;   //不为 null 则把尾部结点设置成新结点的上一个结点 言外之意就是追加一个新的结点 
        if (compareAndSetTail(pred, node)) {   //cas 一次 是否有其他线程更新 tail 
            pred.next = node;
            return node;   //成功就直接返回 不成功 继续 enq 方法 
        }
    }
    enq(node);   //将节点插入队列,必要时进行初始化 
    return node;
}
//将节点插入队列,必要时进行初始化
private Node enq(final Node node) {
    for (;;) {  //自旋 
        Node t = tail;
        if (t == null) {   // 尾部为null 证明 队列为空 则进行初始化 
            // cas 
            if (compareAndSetHead(new Node()))
                tail = head;   //将 head(new Node()) 赋值给 tail 
        } else {
            node.prev = t;   // 与 addWaiter 中的方式相同 cas 设置 tail 
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
//自旋阻塞
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor(); // 获取 node 的 prev 结点(上一个结点)
            if (p == head && tryAcquire(arg)) { // 若上一个为 当前的头部结点 则 tryAcquire 获取
                setHead(node); // 把当前结点设置为 头部结点
                p.next = null; // help GC 
                failed = false; //设置 fail 为 false 表示 已处理 node 无须 cancelAcquire
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&  // 检查和更新未能获取的节点的状态
                parkAndCheckInterrupt()) //锁定对象 this
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node); // 取消结点
    }
}
//检查和更新未能获取的节点的状态
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus; 
    if (ws == Node.SIGNAL) // 若上一个结点状态为 SIGNAL (-1) 则返回 true
        return true;
    if (ws > 0) { //若大于 0 则表示 已取消 则跳过上一个结点
        do {
            node.prev = pred = pred.prev; 
        } while (pred.waitStatus > 0);
        pred.next = node; 
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //更新状态 为 -1 ; new Node 状态为 0
    }
    return false;
}
//锁定对象 并且检查线程是否存活
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

这是 releas  方法  和 acquire 有异曲同工之妙:

public final boolean release(int arg) {
    if (tryRelease(arg)) { //尝试发布 和 tryAcquire 相似 需要子类去实现
        Node h = head; 
        if (h != null && h.waitStatus != 0)  // 判断头部结点 和 头部结点的状态不为0(为 0 则表示未进入阻塞状态)
            unparkSuccessor(h); // 解锁 this
        return true;
    }
    return false;
}
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus; // 获取 head 结点的状态
    if (ws < 0) 若小于 0
        compareAndSetWaitStatus(node, ws, 0); //cas 为 0  
    Node s = node.next; // 获取下一个结点
    if (s == null || s.waitStatus > 0) { //下一个结点为 null 或者已经取消
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev) //则寻找一个 为被取消的结点
            if (t.waitStatus <= 0)
                s = t; 
    }
    if (s != null)
        LockSupport.unpark(s.thread); //解锁
}

 

三、所有提供方法: 

boolean release(int arg)以专属模式发布。
boolean releaseShared(int arg)以共享模式发布。
protected void setState(int newState)设置同步状态的值。
protected boolean isHeldExclusively()返回 true 如果同步仅针对当前(调用)线程进行保存
boolean   isQueued(Thread thread)如果给定的线程当前排队,则返回true。
protected boolean tryAcquire(int arg)尝试以独占模式获取。
boolean tryAcquireNanos(int arg, long   nanosTimeout)尝试以独占模式获取,如果中断则中止,如果给定的超时时间失败
protected int tryAcquireShared(int arg)尝试以共享模式获取。
boolean tryAcquireSharedNanos(int arg, long   nanosTimeout)尝试以共享模式获取,如果中断则中止,如果给定的时间超过,则失败。
protected boolean tryRelease(int arg)

尝试设置状态以独占模式反映版本。

protected boolean

 tryReleaseShared尝试将状态设置为以共享模式反映发布。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值