源码分析AbstractQuenedSynchronized(二)

源码分析AbstractQuenedSynchronized(一)
源码分析AbstractQuenedSynchronized(三)

主要分析AbstractQuenedSynchronized的Condition接口实现类ConditionObject,包括await方法和signal方法

Condition

Condition的产生通过ReentrantLock实例的newCondition方法,而实现ReentrantLock功能的内部类Sync又是AbstractQuenedSynchronized的实现类,由此可以看出:Condition依赖于ReentrantLock实现,而ReentrantLock又依赖于AbstractQuenedSynchronized实现.
在这里插入图片描述

		//##ReentrantLock类
        final ConditionObject newCondition() {
        //实例化一个ConditionObject
            return new ConditionObject();
        }
        
    //##AbstractQuenedSynchronized类
    public class ConditionObject implements Condition, java.io.Serializable {
        /** 条件队列的第一个结点 */
        private transient Node firstWaiter;
        /** 条件队列的最后一个结点 */
        private transient Node lastWaiter;

        public ConditionObject() { }
	}		

由Condition的实现类ConditionObject我们可以看出由两个属性:firstWaiter和lastWaiter,它们都是Node。
在上一篇介绍 AQS 的时候,我们有一个阻塞队列(sync queue),用于保存等待获取锁的线程的队列。这里我们引入另一个概念,叫条件队列(condition queue),如下图所示。
图片源于https://javadoop.com/post/AbstractQueuedSynchronizer-2
图片源于https://javadoop.com/post/AbstractQueuedSynchronizer-2

从图中,我们首先需要了解的是:

  1. 条件队列和阻塞队列的结点都是Node类型,因为条件队列的结点是要转移到阻塞队列中的
  2. Condition的实例是通过ReentrantLock的newCondition方法产生的,一个newCondition产生一个新的Condition实例
  3. 每一个Condition实例都有一个关联的条件队列,由上面的ConditionObject 类我们也可以看出,只有两个属性:条件队列的头结点和末尾结点。如线程 1 调用 condition1.await() 方法即可将当前线程 1 包装成 Node 后加入到条件队列中,然后阻塞在这里,不继续往下执行,条件队列是一个单向链表
  4. 调用condition1.signal() 触发一次唤醒,此时唤醒的是队头,会将condition1 对应的条件队列的 firstWaiter(队头) 移到阻塞队列的队尾,等待获取锁,获取锁后 await 方法才能返回,继续往下执行。

await方法

//##############AbstractQuenedSynchronized类中的ConditionObject类######################## 
        public final void await() throws InterruptedException {
            //1.首先判断线程状态是否中断
            if (Thread.interrupted())
                //中断则直接抛异常
                throw new InterruptedException();
            //2.将当前线程封装成状态为CONDITION的Node并添加到条件队列队尾
            Node node = addConditionWaiter();
            
/*======================================================================
 private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                // 将队列中状态不为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;
        }
======================================================================*/

            //3.调用await方法之前,当前线程必须要持有锁,然后释放锁,这里要完全释放独占锁
            int savedState = fullyRelease(node);//savedState为释放锁之前的state
            
/*======================================================================
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();//返回的savedState为释放锁之前的state
            //释放锁
            //如果一个线程没有获得锁(没有执行lock方法)就执行condition.await()方法,该线程结点会进入等待队列然后执行release方法抛出异常,同时node状态变为cancelled,这个已经入队的结点在后面会被后续结点“请出去”
            
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                //当释放锁失败时
                node.waitStatus = Node.CANCELLED;
        }
    }
======================================================================*/

            int interruptMode = 0;
            //4. 当线程结点不在阻塞队列时将线程挂起
            /*退出当前循环有两种情况:一、当前线程node转移到阻塞队列中 二、线程中断*/
            while (!isOnSyncQueue(node)) 
            
/*======================================================================
    // 当一个node从条件队列转移到阻塞队列时返回true
    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!=null来判断node在阻塞队列,因为CAS操作将自己设为新的tail过程中可能会失败,因此该方法findNodeFromTail从tail往前遍历看能不能在阻塞队列中找到该node
        return findNodeFromTail(node);
    }
======================================================================*/        
  
			{
                //当前线程结点不在阻塞队列中,当前线程挂起
                LockSupport.park(this);
				/*========================
5 有以下三种情况会让 LockSupport.park(this); 这句返回继续往下执行:
(1)常规路径。signal -> 转移节点到阻塞队列 -> 获取了锁(unpark)
(2)线程中断。在 park 的时候,另外一个线程对这个线程进行了中断
(3)signal 的时候我们说过,转移以后的前驱节点取消了,或者对前驱节点的CAS操作失败了
				==========================*/
                //逻辑走到这里,说明线程被唤醒,检查中断状态
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
           
/*======================================================================
 interruptMode 可以取值为 REINTERRUPT(1),THROW_IE(-1),0
         // 1. 如果在 signal 之前已经中断,返回 THROW_IE
         // 2. 如果是 signal 之后中断,返回 REINTERRUPT
         // 3. 没有发生中断,返回 0
	        private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }
        
    final boolean transferAfterCancelledWait(Node node) {
        //由于Signal操作将node的WaitStatus设为0,所以如果下面CAS成功则说明中断发生在signal方法前;CAS失败说明发生在signal方法后
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            //即使中断,也会转移到阻塞队列
            //这里描绘了一个场景,本来有个线程,它是排在条件队列的后面的,但是因为它被中断了,那么它会被唤醒,然后它发现自己不是被 signal 的那个,但是它会自己主动去进入到阻塞队列。
            enq(node);
            return true;
        }

        //逻辑走到这里说明中断发生在signal方法后,但是signal方法可能还没有结束,因此这边自旋等待其完成
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }
======================================================================*/
           
            //6.被唤醒后当前线程node进入阻塞队列,等待获取锁
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                //acquireQueued(node, savedState) 的返回值就是代表线程是否被中断。逻辑到这里,说明线程在signal方法后发生中断
                interruptMode = REINTERRUPT;//将状态设为REINTERRUPT,用于待会重新中断
            if (node.nextWaiter != null) // clean up if cancelled
            //signal方法会将结点从条件队列转移到阻塞队列,同时断开node和后面结点的联系,逻辑走到这里,说明signal之前发生了中断,也需要将节点进行转移到阻塞队列,这部分转移的时候,是没有设置 node.nextWaiter = null 的。
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
                
/*======================================================================
private void reportInterruptAfterWait(int interruptMode)
    throws InterruptedException {
    //THROW_IE则抛异常,REINTERRUPT则重新中断(这里中断的含义是设置一个线程的中断状态为true,并没有抛异常!)
    if (interruptMode == THROW_IE)
        throw new InterruptedException();
    else if (interruptMode == REINTERRUPT)
        selfInterrupt();//  Thread.currentThread().interrupt();
}
======================================================================*/ 
                 
    }


注意到上述await方法的步骤5有三种情况会让 LockSupport.park(this)方法返回,除了响应中断就是利用正常途径signal方法了,下面开始分析Signal方法


signal方法

//##承接await方法的步骤4:LockSupport.park(this); 
//唤醒等待最久的线程,将这个线程node从条件队列中转移到阻塞队列中
        public final void signal() {
         // 调用 signal 方法的线程必须持有当前的独占锁
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }    

/*======================================================================
        private void doSignal(Node first) {
            do {
            //将firstWaiter 指向下一个Waiter,因为它就要转移到阻塞队列了
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                //first即将离开,断开和后面的关系
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);//这里 while 循环,如果 first 转移不成功,那么选择 first 后面的第一个节点进行转移,依此类推
        }
    
    //返回return代表:成功将node转移到阻塞队列       false:该节点已经取消,不需要转移了    
    final boolean transferForSignal(Node node) {
    // CAS 如果失败,说明此 node 的 waitStatus 已不是 Node.CONDITION,说明节点已经取消,
    // 既然已经取消,也就不需要转移了,方法返回,转移后面一个节点
    // 否则,将 waitStatus 置为 0
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        Node p = enq(node);//自旋进入阻塞队列的队尾,p为node在阻塞队列的前驱结点
        int ws = p.waitStatus;
       
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            //正常情况下不会进入该逻辑,但是如果前驱结点取消或者CAS设置前驱结点为SINGAL失败则唤醒当前线程,直接进入步骤4的下一步
            LockSupport.unpark(node.thread);
        return true;
    }
======================================================================*/    

总结

Conditiond的await和signal过程:
await
1.首先判断线程状态是否中断
2.将当前线程封装成状态为CONDITION的Node并添加到条件队列队尾
3.释放锁,这里要完全释放独占锁(调用await方法之前,当前线程必须要持有锁,否则在尝试释放锁时就会抛异常)
4. 当线程结点不在阻塞队列时将线程挂起
5.signal方法将这个线程node从条件队列中转移到阻塞队列中(转移到阻塞队列的方式有两个:signal方式和中断方式)
6.在阻塞队列中等待重新获取锁

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章