并发编程学习笔记之十—并发工具Condition源码分析

在并发编程中,synchronized是和wait()、notify()、notifyAll()实现的等待通知机制。而比synchronized更强大的Lock同样实现了自己的最佳队友那就是Condition的await()、signal()、signalAll()。下面就以可重入锁ReentrantLock为例分析下Lock和Condition如果实现线程间的通信。

Condition与Lock一样也是接口,来看下接口的具体方法

//线程等待
void await() throws InterruptedException;
//现在等待
void awaitUninterruptibly();
//设置超时时间的等待
long awaitNanos(long nanosTimeout) throws InterruptedException;
//返回等待状态
boolean await(long time, TimeUnit unit) throws InterruptedException;
//
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一条线程
void signal();
//唤醒所有线程
void signalAll();

在可重入锁ReentrantLock中提供了 newCondition()方法实例化ConditionObject对象,这也就是提供了一个和Lock绑定的Condtion。而ConditionObject是队列同步器AQS(AbstractQueuedSynchronizer)的内部类,实现了Condition和Serializable序列化接口。主要来分析下await()、signal()、signalAll()三个方法。

两个重要的变量:

在Condtion类中实现了一个单链表实现的条件队列

//条件队列的首节点
private transient Node firstWaiter;
//条件队列的尾节点
private transient Node lastWaiter;

节点的等待状态

/**waitStatus value to indicate thread has cancelled 
 *因为中断或者超时,该线程已经取消
 */
static final int CANCELLED =  1;

/** waitStatus value to indicate successor's thread needs unparking 
 *现在正在被阻塞或者已经阻塞
 */
static final int SIGNAL    = -1;

/** waitStatus value to indicate thread is waiting on condition 
 *表示该线程被处于条件队列中,就是因为调用了Condition.awaut而表示阻塞
 */
static final int CONDITION = -2;
/**
 * waitStatus value to indicate the next acquireShared should
 * unconditionally propagate
 * 传播共享锁
 */
static final int PROPAGATE = -3;

线程进入等待 awati()

  1. 判断线程是否已经中断,中断的线程直接抛异常
  2. 将当前线程加入到阻塞队列中,将入阻塞队列之前调用(3)unlinkCancelledWaiters()清除掉阻塞队列中的状态为非阻塞的节点;新建当前线程的节点,节点状态为阻塞,如果当尾节点为空,则当前节点赋值首节点,否则设置为尾节点的后继节点,当前节点为新的尾节点返回,继续执行方法(4)fullyRelease。
  3. unlinkCancelledWaiters是重构队列的方法,将不是阻塞状态的节点剔除掉,里边有一个很重要的条件t.waitStatus != Node.CONDITION
  4. fullyRelease是放锁的方法与unlock调用的方法相同。接下来执行(5)
  5. isOnSyncQueue(Node node)主要判断线程是否在同步阻塞队列中,这一块也是笔记晦涩难以看懂的内容,涉及节点的状态转换,主要有三种情况:                                                                                                                                                              5.1.如果节点状态是Node.CONDITION 或者node.prev == null表示节点未在条件队列中,返回为false                                   5.2.节点的后继节点不为null,表示节点在同步等待队列中,返回为true                                                                                      5.3.还有一种情况是,前继节点不为空,但是也可能不在条件队列中,因为CAS可能会失败,
  6. 循环去判断节点的位置循环去判断节 点的位置,如果当前节点在是等待队列的尾节点,返回true,如果等待队列尾节点为空,返回false
  7. 检查线程是否中断,如果未中断则返回为0,如果未中断,则结束循环。
        
         public final void await() throws InterruptedException {
            //1.判断线程是否中断,如果中断直接抛出异常
            if (Thread.interrupted())
                throw new InterruptedException();
            //2.将新节点加入到条件队列中
            Node node = addConditionWaiter();(2)
            //4.释放锁
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            //5.循环判断节点是否在同步队列中
            while (!isOnSyncQueue(node)) {
                //不在同步队列中,则阻塞线程
                LockSupport.park(this);
                //检查线程是否中断,如果未中断则返回为0
                //5-1
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            //7.唤醒线程如果需要继续执行,仍需要获得锁,中断位不是THROW_IE
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;//赋值状态为中断
            //节点的后继节点不为空,也就不是尾节点
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();//8.清空阻塞队列
            //节点状态不是0
            if (interruptMode != 0)
                //9.根据此时线程的状态抛出异常或是返回中断位
                reportInterruptAfterWait(interruptMode);
        }
            
        //2.将新节点加入到条件队列尾部
        private Node addConditionWaiter() {
            //获取尾节点
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            //如果节点不为空,节点的状态为Node.CONDITION(表示线程正在条件队列中)
            if (t != null && t.waitStatus != Node.CONDITION) {
                //2-1去除非条件队列的节点,调整队列结构
                unlinkCancelledWaiters();
                //重新返回尾节点
                t = lastWaiter;
            }
            //创建关联当前线程的新节点,设置为CONDITION
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            //如果尾节点为空,将新节点设置为首节点
            if (t == null)
                firstWaiter = node;
            else
                //否则直接追加首节点后
                t.nextWaiter = node;
            //将新节点设置到尾节点直接返回
            lastWaiter = node;
            return node;
        }

        //2-1删除不是不符合条件的队列
        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//如果现在首节点是CONDITION,直接将节点赋值给临时节点,后继节点变为首节点继续循环
                    trail = t;
                t = next;
            }
        }


   //3.await实际就是wait,而wait会释放锁,所以这一步会释放资源,修改state的值,
   //可以参考unlock方法分析,如果出现问题会修改节点状态为Node.CANCELLED中断或者超时
   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;
        }
    }
    //5.判断线程是否在同步队列中
    final boolean isOnSyncQueue(Node node) {
        //如果节点状态是Node.CONDITION 或者node.prev == null表示节点未在同步队列中
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        //节点的后继节点不为null,表示节点在同步队列中
        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.
         */
        //还有一种情况是,前继节点不为空,next为空的情况,但是也可能在同步队列中,因为加队同步队列的CAS可能会失败,那加入节点的位置,可能在队列尾结点也可能在队列中,为了保险需从尾部开始查找节点
        //6.循环去判断节点的位置
        return findNodeFromTail(node);
    }
    //5-1
    private int checkInterruptWhileWaiting(Node node) {
        return Thread.interrupted() ?
            (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
              0;
    }
    //5-2
    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;
    }
   
    //6.循环去判断节点的位置,如果当前节点在是同步队列的尾节点,返回true,如果同步队列尾节点为空
    //返回false
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }
     //7.
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
     //8.
     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;
            }
        }
     //9
     private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }

唤醒一个等待线程signal()

(1)只有获得锁的线程才能执行signalAll,否则会抛错

(2)换线线程的操作doSignal(Node first),循环剔除不在阻塞状态的线程,循环将要被通知的线程从阻塞队列中删除

(3)transferForSignal(Node node)将被通知的节点加入到等待队列中,唤醒被通知节点的线程。

signal和signalAll的区别主要signal只会将第一个等待通知的节点从阻塞队列中删除,而signalAll是将循环将要通知的节点从阻塞队列中移除,而通知线程唤醒的方法是一致的。

   public final void signal() {
      //判断当前线程是否是占用线程(是否获得锁),如果不是则抛错
      if (!isHeldExclusively())//(1)
          throw new IllegalMonitorStateException();
      Node first = firstWaiter;
      if (first != null)
           doSignal(first);(2)唤醒线程真正的方法
   }
   //(1)判断当前线程是否获取锁
   protected final boolean isHeldExclusively() {
      // While we must in general read state before owner,
      // we don't need to do so to check if current thread is owner
     return getExclusiveOwnerThread() == Thread.currentThread();
   }
   
   //(2)唤醒线程
   private void doSignal(Node first) {
      do {
         //将要被通知的节点的后继节点设置为等待队列首节点,如果当前节点的下一个节点为空
         if ( (firstWaiter = first.nextWaiter) == null)
             //设置等待队列的尾结点为空
             lastWaiter = null;
            //将要被通知的节点的后继节点置为空,将被通知的节点脱离等待队列
             first.nextWaiter = null;
      //循环条件:剔除不是阻塞状态的节点,同时当前节点不为空
      } while (!transferForSignal(first) &&//(3)
         (first = firstWaiter) != null);
   }
   //(3)修改节点状态,唤醒线程
   final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        //通过原子CAS操作修改节点状态,如果修改失败(表示线程已经不是阻塞状态,不在阻塞队列中)返回false,返回循环(3)继续判断循环条件
        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;//获取节点的等待状态
        //节点状态>0,表示节点已经被取消或者已经不能把节点状态修改为阻塞
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            //唤醒线程
            LockSupport.unpark(node.thread);
        return true;
    }
   
    
   
 

通知所有线程signalAll()

(1)只有获得锁的线程才能执行signalAll,否则会抛错

(2)换线线程的操作doSignal(Node first),循环剔除不在阻塞状态的线程,循环将要被通知的线程从阻塞队列中删除

(3)transferForSignal(Node node)将被通知的节点加入到等待队列中,唤醒被通知节点的线程。

signal和signalAll的区别主要signal只会将第一个等待通知的节点从阻塞队列中删除,而signalAll是将循环jian

    public final void signalAll() {
    //判断当前线程是否是占用线程(是否获得锁),如果不是则抛错
      if (!isHeldExclusively())//(1)
             throw new IllegalMonitorStateException();
      Node first = firstWaiter;
      if (first != null)
           doSignalAll(first);//(2)唤醒线程的主要方法
    }
   //(1)判断当前线程是否获取锁
   protected final boolean isHeldExclusively() {
      // While we must in general read state before owner,
      // we don't need to do so to check if current thread is owner
     return getExclusiveOwnerThread() == Thread.currentThread();
   }
    
    //(2)唤醒线程
    private void doSignalAll(Node first) {
      //将首节点和尾节点都置为空
      lastWaiter = firstWaiter = null;
      do {
           //将被通知节点从阻塞队列中移除
           Node next = first.nextWaiter;
           first.nextWaiter = null;
           transferForSignal(first);//(3)加入等待队列,唤醒线程
           first = next;
        //循环条件:被通知节点不为空
         } while (first != null);
    }
    //(3)
    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;
    }

 

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