併發編程學習筆記之十—併發工具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;
    }

 

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