AQS分析

常说的AQS工具类就是 AbstractQueuedSynchronizer� 常见的ReentrantLock,Semaphore,CountDownLatch 都是基于它实现的并发控制,你也可以基于AQS比较简单的实现自定义的锁,下面看下AQS里面一些主要方法是怎么进行实现的。

主要结构成员Node

简单看下Node 的成员变量

static final class Node {
	// 共享模式
    static final Node SHARED = new Node();
    // 独占
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled. */
    // waitStatus 一种状态代表取消
    static final int CANCELLED =  1;
    // waitStatus 的一种状态,需要通过unpark唤醒后面的线程
    static final int SIGNAL    = -1;
    // waitStatus 等待条件,即等待唤醒
    static final int CONDITION = -2;
    // 共享模式下的,要传播到下一个获取共享锁的
    static final int PROPAGATE = -3;

    // 状态,通过waitStatus来判断Node的状态是怎么的
    volatile int waitStatus;


    // 前一个节点
    volatile Node prev;

    // 下一个节点
    volatile Node next;

    // Node的线程
    volatile Thread thread;

    // 下一个
    Node nextWaiter;
}

可以看到里面定义了一个Node结构,存放了waitStatus来表明现在节点的状态,-1,-2,-3 代表不同的等待状态,1为取消,然后定义了持有的线程,和前后节点,即是个双向链表结构,简单了解下结构,下面看下方法里面是怎么用的

AQS的成员变量

// 头节点
private transient volatile Node head;

// 尾节点
private transient volatile Node tail;

// state,状态,之前看到了在Reentrant里面用来记录持有锁的次数
private volatile int state;

直接按照主要方法存在顺序来分析吧

enq� 插入一个node节点

private Node enq(Node node) {
    for (;;) {
    // 找到尾节点
        Node oldTail = tail;
    // 不为空
        if (oldTail != null) {
            // 先把node的前节点设置为原来的尾节点
            node.setPrevRelaxed(oldTail);
            // 然后再cas设置当前node节点为尾节点,这时候前面的next节点并没有指向node,
            // 但是尾节点的prev已经指向原来的尾节点了,先改prev,再改next
            if (compareAndSetTail(oldTail, node)) {
                // next指针维护
                oldTail.next = node;
                return oldTail;
            }
        } else {
            // 这里的初始化,是把头尾节点都设置为一个空的Node,两者指向同一个
            initializeSyncQueue();
        }
    }
}
// 插入一个节点,看起来跟enq基本一致就是一个mode的差别,代表了节点是独占还是共享的
private Node addWaiter(Node mode) {
    Node node = new Node(mode);

    for (;;) {
        Node oldTail = tail;
        if (oldTail != null) {
            node.setPrevRelaxed(oldTail);
            if (compareAndSetTail(oldTail, node)) {
                oldTail.next = node;
                return node;
            }
        } else {
            initializeSyncQueue();
        }
    }
}

enq插入一个node节点,原来头尾节点不存在的时候会初始化一个空的Node节点,存在之后会先把node的prev指向原来的tail,然后cas更改tail指向,再修改next指针,这也是AQS需要从后向前遍历的一部分原因

unparkSuccessor 唤醒节点的后续节点

private void unparkSuccessor(Node node) {
    
    int ws = node.waitStatus;
    // 如果小于0代表的取消,设置为0,为0的时候不需要唤醒后续节点,可以在后面得到验证
    if (ws < 0)
        node.compareAndSetWaitStatus(ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
	// 获取下一个节点
    Node s = node.next;
	// 如果节点为空,或者节点取消状态的话
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从尾部开始向前遍历
        for (Node p = tail; p != node && p != null; p = p.prev)
            // 只查找状态小于等于0的,这是正常的,1的是取消的
            if (p.waitStatus <= 0)
                s = p;
    }
	// 对节点调用unpark
    if (s != null)
        LockSupport.unpark(s.thread);
}

unparkSuccessor就是为了唤醒后续节点,如果ws小于0就设置为0,然后找到next节点,如果next节点为null或者是取消状态,那么开始从后向前遍历,记录不取消节点,知道遍历到本节点,然后对其唤醒

doReleaseShared 释放共享次数

private void doReleaseShared() {
    
    for (;;) {
        Node h = head;
        // 判断存在数据,即头尾不同,头不为空
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL){
                // 设置为0,失败的话下次循环
                if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // 成功的话对其唤醒
                unparkSuccessor(h);
            }
            // 如果为0 的话,就尝试设置为PROPAGATE
            else if (ws == 0 &&
                     !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        //循环检测到head没有变化时就会退出循环。
        //注意,head变化一定是因为:acquire thread被唤醒,之后它成功获取锁,
        //然后setHead设置了新head。而且注意,只有通过if(h == head) break;
        //即head不变才能退出循环,不然会执行多次循环
        if (h == head)                   // loop if head changed
            break;
    }
}

setHeadAndPropagate

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    // 设置新的头节点
    setHead(node);
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

cancelAcquire 取消节点的等待操作

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;

    node.thread = null;

    // Skip cancelled predecessors
    Node pred = node.prev;
	// 修改prev节点,如果prev也取消了就继续向前
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

	// 记录引用
    Node predNext = pred.next;

	// 设置节点为取消状态
    node.waitStatus = Node.CANCELLED;

    // 如果是tail节点,直接设置尾节点为前一个节点
    if (node == tail && compareAndSetTail(node, pred)) {
        // 设置next为空
        pred.compareAndSetNext(predNext, null);
    } else {
    	// 非尾节点,或者就是设置失败的
        int ws;
        // 前节点不是头节点并且(是signal状态或者设置signal状态成功)并且对应线程不为空
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
            pred.thread != null) {
            // 下面的节点
            Node next = node.next;
            // 不为空,并且是正常节点,建立next关系
            if (next != null && next.waitStatus <= 0)
                pred.compareAndSetNext(predNext, next);
        } else {
            unparkSuccessor(node);
        }
    	// 断开next,自循环帮助gc
        node.next = node; 
    }
}

shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    // 取消状态,那么就处理下前节点一直向前连接
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        // next指向当前的
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
    }
    return false;
}

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
    boolean interrupted = false;
    try {
        for (;;) {
            // 前节点
            final Node p = node.predecessor();
            // 前面是头节点,然后自己尝试获取,这个由继承的类实现尝试获取的逻辑
            if (p == head && tryAcquire(arg)) {
                // 获取成功,设置头节点,
                setHead(node);
                p.next = null; // help GC
                return interrupted;
            }
            // 获取失败的话
            if (shouldParkAfterFailedAcquire(p, node))
                // 记录是不是被打断了interrupted,外部线程可以interrupt
                // 里面进行了park ,等待后续唤醒
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        // 出现异常,就取消排队
        cancelAcquire(node);
        //如果被打断了,就自己
        if (interrupted)
            selfInterrupt();
        throw t;
    }
}

doAcquireInterruptibly 获取一定数量,声明抛出的异常

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

doAcquireNanos 指定时间获取

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                return true;
            }
            // 每次循环记录时间
            nanosTimeout = deadline - System.nanoTime();
            // 超时之后取消
            if (nanosTimeout <= 0L) {
                cancelAcquire(node);
                return false;
            }
            // 这里的park就park一定的时间
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

doAcquireShared

private void doAcquireShared(int arg) {
	// 共享节点
    final Node node = addWaiter(Node.SHARED);
    boolean interrupted = false;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                // 释放指定次数
                int r = tryAcquireShared(arg);
                if (r >= 0) 
                    // 设置当前为头,这也是之前判断头节点是不是变化的原因吧
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node))
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    } finally {
        if (interrupted)
            selfInterrupt();
    }
}

doAcquireSharedNanos一定时间内获取共享锁,超时取消

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    // 计算超时的时间点
    final long deadline = System.nanoTime() + nanosTimeout;
    // 加上的共享节点
    final Node node = addWaiter(Node.SHARED);
    try {
        for (;;) {
            // 节点的prev
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    return true;
                }
            }
            // 超时时间判断,超时取消
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L) {
                cancelAcquire(node);
                return false;
            }
            // park一定的时间
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

总结

简单过了下代码,简单总结下吧,先看独占锁,共享锁和Condition 后面再单独分析下吧,主要就是acquire 和release,这里面acquire在获取资源,获取不到的时候就加入队列,使用的addWaiter,把对应的节点加入到队列尾部,然后对队列中的节点进行等待获取锁,采用signal控制唤醒后面的节点,当前节点的前节点是头节点的时候,被唤醒会进行尝试获取锁,如果获取失败,可能被别的非公平锁获取了,继续等待,存在个问题,如果队列里面所有的节点都循环获取锁,那会很占用cpu,也没意义,主要也是会按照队列里面的顺序(非公平的也只有入队之前的一次抢占机会,入队之后就需要按照顺序等待),所以就采用前面节点唤醒后面节点的方法,采用unpark进行唤醒,后面不该到的节点就采用park方法,等待前节点执行完唤醒自己。
AQS采用的是双向链表存储,添加节点的时候会先修改prev指针,然后再改的next指针,所以存在有时候next指针没值,但是实际上已经有值的情况,还有一种情况是节点取消,可能是超时取消,就会把节点的next值改了指向自己(非头尾节点),但是prev指针不动,所以在取next为空或者为取消状态的时候,会采用从后向前遍历的方法获取下一个需要唤醒的节点。
这次读的得到一个大致的原理,后续再深入分析下吧 ,再理解下共享的实现模式,现在没太清楚共享和独占的区别,共享似乎是把所有等待的一下子唤醒了,还有Condition后面再单独分析下,现在看到的就是类似synchronized配套使用的wait和notify,然后lock就配套了await 和signal 这样,就是在获取到锁的时候对其进行await释放锁,然后signal提醒其他正在等待的起来继续执行。

你可能感兴趣的:(锁,java,AQS,并发)