ReentrantLock源码解读(3)——Condition

ReentrantLock源码系列
ReentrantLock源码解读(1)——CAS
ReentrantLock源码解读(2)——ReentrantLock源码与AQS
ReentrantLock源码解读(3)——Condition

Condition是除了object的await/notify之外的另一种信号机制,通常与ReentrantLock连用。先给给例子

public class LockTest implements Runnable{

    public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();

    @Override
    public void run() {
        lock.lock();
        try {
            System.out.println("before await");
            condition.await();
            System.out.println("await end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {

            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockTest test = new LockTest();
        Thread thread = new Thread(test);
        thread.start();
        Thread.sleep(2000);
        lock.lock();
        condition.signal();
        lock.unlock();
    }
}
  • 源码
    从newCondition()开始看。
	public Condition newCondition() {
        return sync.newCondition();
    }

	...
	final ConditionObject newCondition() {
            return new ConditionObject();
    }

最后只是new了一个ConditionObject对象,后续的await方法,notify方法都是这个类所带的。这个类是AQS的一个内部类。

        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

它里面也维护了一个node队列。
await
现在来看await方法。

        /**
         * Implements interruptible condition wait.
         * <ol>
         * <li> If current thread is interrupted, throw InterruptedException.
         * <li> Save lock state returned by {@link #getState}.
         * <li> Invoke {@link #release} with saved state as argument,
         *      throwing IllegalMonitorStateException if it fails.
         * <li> Block until signalled or interrupted.
         * <li> Reacquire by invoking specialized version of
         *      {@link #acquire} with saved state as argument.
         * <li> If interrupted while blocked in step 4, throw InterruptedException.
         * </ol>
         */
        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

在await方法中,基本上都是调的其他方法,没有多少自己的逻辑。一个个来看,它调的方法

        /**
         * Adds a new waiter to wait queue.
         * @return its new wait node
         */
        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            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;
        }

addConditionWaiter这个方法就是创建一个node,然后添加到队列中,它只操作了nextWaiter ,并没有操作指向上一个节点的prev。所以,它并不像线程的等待队列一样是双向的,它是单向的。unlinkCancelledWaiters后面再看。

    /**
     * Invokes release with current state value; returns saved state.
     * Cancels node and throws exception on failure.
     * @param node the condition node for this wait
     * @return previous sync state
     */
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }

fullyRelease是一个释放锁的方法,因为当前线程await了,肯定要把锁释放出来。而且它是完整释放,也就是不管锁重入了几次都一起释放。同时把重入的次数返回。仔细想一想,现在加了多重锁一起释放,当被signal的时候,这些锁得加上呀,所以这个加锁次数得返回

    /**
     * Returns true if a node, always one that was initially placed on
     * a condition queue, is now waiting to reacquire on sync queue.
     * @param node the node
     * @return true if is reacquiring
     */
    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         */
        return findNodeFromTail(node);
    }

    /**
     * Returns true if node is on sync queue by searching backwards from tail.
     * Called only when needed by isOnSyncQueue.
     * @return true if present
     */
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

isOnSyncQueue这个方法呢,就是判断这个node在不在Sync也就是锁的等待队列中。在这里呢有两个队列,一个是锁的等待队列,一个是condition的等待队列。

/**
         * Checks for interrupt, returning THROW_IE if interrupted
         * before signalled, REINTERRUPT if after signalled, or
         * 0 if not interrupted.
         */
        private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }
        
...

    /**
     * Transfers node, if necessary, to sync queue after a cancelled wait.
     * Returns true if thread was cancelled before being signalled.
     *
     * @param node the node
     * @return true if cancelled before the node was signalled
     */
    final boolean transferAfterCancelledWait(Node node) {
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            enq(node);
            return true;
        }
        /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
         */
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }

checkInterruptWhileWaiting这是一个检测中断的方法,如果线程阻断了,就把他给移到线程等待队列。主要是线程中断断的时候的一个特殊情况处理。
condition类里面定义了两个中断类型的常量

        /** Mode meaning to reinterrupt on exit from wait */
        private static final int REINTERRUPT =  1;
        /** Mode meaning to throw InterruptedException on exit from wait */
        private static final int THROW_IE    = -1;

分别是wait状态下中断退出和wait状态下抛中断异常退出。
acquireQueued这个方法在上一篇AQS源码中有,就是一个自旋获取锁。

        /**
         * Unlinks cancelled waiter nodes from condition queue.
         * Called only while holding lock. This is called when
         * cancellation occurred during condition wait, and upon
         * insertion of a new waiter when lastWaiter is seen to have
         * been cancelled. This method is needed to avoid garbage
         * retention in the absence of signals. So even though it may
         * require a full traversal, it comes into play only when
         * timeouts or cancellations occur in the absence of
         * signals. It traverses all nodes rather than stopping at a
         * particular target to unlink all pointers to garbage nodes
         * without requiring many re-traversals during cancellation
         * storms.
         */
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }

unlinkCancelledWaiters取消所有等待节点。

        /**
         * Throws InterruptedException, reinterrupts current thread, or
         * does nothing, depending on mode.
         */
        private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }

reportInterruptAfterWait方法名意思是中断上报。就是根据中断状态做相应处理,该throw就throw,该interrupt就interrupt。

单个方法都看完了,就来看await流程了。流程很简单就不画流程图了。

await流程
1、加入condition等待队列
2、释放现在的锁并记录锁的次数
3、一个循环,里面有park操作来阻塞,退出循环的条件是node在线程等待队列中,或线程中断。根据这个循环,可以大致猜到signal的操作就是让node出现在线程等待队列中,然后可以退出这个循环。
4、重新自旋加锁,加锁次数就是释放锁的次数
5、condition等待队列没有后续节点等待了,就情况等待队列
6、如果有中断,或异常,进行相应处理。

signal
看signal方法

        /**
         * Moves the longest-waiting thread, if one exists, from the
         * wait queue for this condition to the wait queue for the
         * owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

找到队列的第一个节点,然后doSignal方法

        /**
         * Removes and transfers nodes until hit non-cancelled one or
         * null. Split out from signal in part to encourage compilers
         * to inline the case of no waiters.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

从condition等待队列移除这个node然后调transferForSignal方法

    /**
     * Transfers a node from a condition queue onto sync queue.
     * Returns true if successful.
     * @param node the node
     * @return true if successfully transferred (else the node was
     * cancelled before signal)
     */
    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

这个方法先把node的waitstatus变成0,然后加入到线程等待队列中,然后waitstatus改为SIGNAL,然后unpark解除阻塞。
整个signal的流程就是把node从condition等待队列移入线程等待队列,然后unpark。

刚好在await中有一个是否在线程等待队列的判断,signal操作就让await操作可以退出这个循环阻塞,然后去争锁。

signalAll方法再看看。

		public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }

        /**
         * Removes and transfers all nodes.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }

与signal不同的是,它把所有的node从condition等待队列移入到线程等待队列。
condition源码阅读结束,剩下一些方法就不看了。

最后
到此,整个ReentrantLock源码解读结束了,整个加锁流程,condition流程都了解了。
对于AQS来说,我们只了解了它的独占锁部分,它还有共享锁部分。如果想了解共享锁部分建议阅读Semaphore这个类的源码。
独占锁和共享锁在源码上的区别,这里提一下。独占锁,state为0表示没有加锁,大于0的其他数字表示加了几层锁,加一次锁,state+1。共享锁,state等于0表示没有锁资源,大于0的其他数表示有多少个锁资源,可以让几个线程加锁,加一次锁,state-1

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页