浅析AQS

AQS

抽象队列同步器,定义了一套多线程访问共享资源的同步器,许多同步类的实现都依赖于AQS,如ReentrantLock、Semaphore。自定义同步器需要实现共享资源state的获取与释放,等待队列的维护(入队与唤醒出队)由AQS实现。

结点状态(int类型,初始化为0)
  • CANCELLED(1)
    结点取消调度,当timeout或者被中断的情况下,会触发变更为此状态
  • SIGNAL(-1)
    结点等待被通知唤醒,当结点入列时会将前一结点的状态变更为此状态
  • CONDITION(-2)
    结点等待在Condition上,当其他线程调用Condition的signal()方法后,该状态的结点会从等待队列转义到同步队列
  • PROPAGATE(-3)
    共享模式
  • 0
    新结点的默认状态
成员变量
  • head
    头结点
  • tail
    尾结点
  • state
    同步资源
成员方法:
  • acquire(int)
    以独占的方式获取资源,若获取成功则直接返回,否则进入等待队列直至被唤醒再次尝试获取资源。
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
      selfInterrupt();
   }
}
  • tryAcquire(int)
    自定义实现方法,尝试获取资源。成功返回true,失败返回false。注意该方法需保证线程安全。
  • addWaiter(Node)
    将当前线程的结点加入到队尾,并返回当前线程的结点。
private Node addWaiter(Node mode) {
    //根据给定模式构造结点(EXCLUSIVE(独占)和SHARED(共享))
    Node node = new Node(Thread.currentThread(), mode);
    //尝试快速将新结点插入到队尾
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //若快速插入结尾失败(如队列收尾节点未初始化),则调用enq(Node)方法插入队尾
    enq(node);
    return node;
}
  • enq(Node)
执行完之后队列为:EmptyNode -> Node1
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) {
            //当尾结点为空,即队列为空时,初始化设置头尾结点为空结点,进入第二次循环
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //插入新结点到尾结点后
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
  • acquireQueued(Node, arg)
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        //该方法会在第一次执行循环体后,自旋进入第二次循环并在shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()处挂起
        for (;;) {
            //获取前结点前一个结点
            final Node p = node.predecessor();
            //仅头结点的下一个结点可执行tryAcquire方法
            if (p == head && tryAcquire(arg)) {
                //获取资源成功,设置当前节点为头结点,头结点GC,return false
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //把当前节点和前一结点作为参数,调用shouldParkAfterFailedAcquire
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • shouldParkAfterFailedAcquire(Node pred, Node node)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //前一结点的waitStatus
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        若为SIGNAL,则返回true,挂起等待唤醒
        return true;
    if (ws > 0) {
        //若>0,则说明该结点取消调度,删除该结点直至不出现>0的情况
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //设置前一结点的waitStatus为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
  • parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
    //挂起当前线程
    LockSupport.park(this);
    //被唤醒或中断后返回当前线程的中断状态
    return Thread.interrupted();
}
  • release(int)
public final boolean release(int arg) {
    //尝试释放资源,成功则唤醒头结点的下一结点,并设置头结点的waitStatus为0。
    //唤醒头结点的下一结点(记为node1)后,node1继续进入自旋尝试获取资源。若成功获取则将首结点设置为自己,失败继续挂起。
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • unparkSuccessor(Node node)
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        //设置结点waitStatus为0
        compareAndSetWaitStatus(node, ws, 0);
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值