ReentrantLock—Condition源碼解讀

          本文是在學習中的總結,歡迎轉載但請註明出處:http://blog.csdn.net/pistolove/article/details/105031259


1、前言

Condition實現關鍵:等待隊列。

  • 等待隊列是一個FIFO的隊列,隊列中每個節點包含一個線程引用,該線程就是在Conditon對象上等待的線程。一個Condition對象包含一個等待隊列,Condition擁有首節點(firstWaiter)和尾結點(lastWaiter)。當線程調用Conditon.await()方法,將會以當前線程構造節點,並將節點從尾部加入到等待隊列。

在這裏插入圖片描述

Object和Condition對比:

  • Object是java底層級別的;Condition是語言級別的,具有更高的可控制性和擴展性。
  • Object的wait和notify/notify是與對象監視器配合完成線程間的等待/通知機制;Condition與Lock配合完成等待通知機制。
  • Condition能夠支持不響應中斷;Object方式不支持;
  • Condition能夠支持多個等待隊列(new 多個Condition對象);Object方式只能支持一個;
  • Condition能夠支持超時時間的設置;Object方式不支持。

2、Condition代碼示例

  • 基於Condition生產者消費者代碼實現如下所示,先了解一下Condition是如何使用的。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 基於Condition生產者消費者代碼實現
 *
 * @param <T>
 */
public class ConditionDemo<T> {

    private ReentrantLock reentrantLock = new ReentrantLock();

    private Condition consumerCondition = reentrantLock.newCondition();

    private Condition producerCondition = reentrantLock.newCondition();

    private int count, addIndex, removeIndex;

    private Object[] goods;

    public ConditionDemo(int size) {
        goods = new Object[size];
    }

    /**
     * 生產商品
     *
     * @param t
     */
    public void produce(T t) {
        reentrantLock.lock();
        try {
            while (count == goods.length)
                producerCondition.await();

            goods[addIndex] = t;
            if (++addIndex == goods.length) {
                addIndex = 0;
            }
            ++count;
            System.out.println("produce: " + t);
            consumerCondition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    /**
     * 消費產品
     *
     * @return
     */
    public T consume() {
        reentrantLock.lock();
        try {
            while (count == 0)
                consumerCondition.await();
            Object x = goods[removeIndex];
            if (++removeIndex == goods.length) {
                removeIndex = 0;
            }
            --count;
            producerCondition.signal();

            System.out.println("consume: " + x);
            return (T) x;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
        return null;
    }

    public static void main(String[] args) {
        ConditionDemo<String> conditionDemo = new ConditionDemo(10);

        new Thread() {
            @Override
            public void run() {
                int i = 1;
                while (i != 100) {
                    conditionDemo.produce("" + i++);
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                while (true) {
                    conditionDemo.consume();
                }
            }
        }.start();
    }
}

3、類關係圖

  • new Condition()其實是創建了一個ConditionObject,ConditionObject實現了Condition接口和Serializable接口。
ConditionObject
Condition
await
await:time
signal
signalAll

4、源碼解讀

  • await()方法源碼解讀

    • 使當前線程進入等待隊列,並釋放鎖,同時線程狀態變爲等待狀態;相當於同步隊列的首節點移到了Condition的等待隊列中。
    public final void await() throws InterruptedException {
        // 線程中斷則拋出異常
        if (Thread.interrupted())
            throw new InterruptedException();
        
        // 當前線程加入Conditon等待隊列
        Node node = addConditionWaiter();
        
        // 釋放同步狀態,即釋放同步鎖;喚醒當前線程的節點(頭節點)的後繼節點
        int savedState = fullyRelease(node);
        
        int interruptMode = 0;
        
        // 如果節點不在同步隊列中
        while (!isOnSyncQueue(node)) {
            // 阻塞當前線程
            LockSupport.park(this);
            
            // THROW_IE if interrupted before signalled
            // REINTERRUPT if after signalled
            // 如果中斷被喚醒,循環停止 
            // 判斷此次線程的喚醒是否因爲線程被中斷, 
            // 若是被中斷, 則會在checkInterruptWhileWaiting的transferAfterCancelledWait進行節點的轉移; 
            // 返回值interruptMode != 0
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                // 通過線程中斷的方式進行喚醒, 並且已經進行node的轉移, 轉移到 Sync Queue裏面
                break;
        }
        
        // 調用 acquireQueued在Sync Queue裏面進行獨佔鎖的獲取, 返回值表明在獲取的過程中有沒有被中斷過
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        
        // 判斷線程的喚醒是中斷還是signal
        // 如通過中斷喚醒的話, 此刻代表線程的Node在Condition Queue與Sync Queue裏面都會存在
        if (node.nextWaiter != null) // clean up if cancelled
            // cancelled節點的清除
            unlinkCancelledWaiters();
        
        // 通過中斷的方式喚醒線程
        if (interruptMode != 0)
            // 根據 interruptMode 的類型決定是拋出異常, 還是自己中斷一下
            reportInterruptAfterWait(interruptMode);
    }

    // 加入到等待隊列
    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;
    }

    // 移除狀態爲已取消的節點                                  
    private void unlinkCancelledWaiters() {
        Node t = firstWaiter;
        Node trail = null;
        while (t != null) {
            Node next = t.nextWaiter;
            // 如果狀態不是條件等待
            if (t.waitStatus != Node.CONDITION) {
                //刪除next引用
                t.nextWaiter = null;
                if (trail == null)
                    firstWaiter = next;
                else
                    trail.nextWaiter = next;
                if (next == null)
                    lastWaiter = trail;
            }
            else
                trail = t;
            t = next;
        }
    }
    

    // 釋放鎖
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            // 獲取當前同步狀態
            int savedState = getState();
            
            //釋放鎖
            if (release(savedState)) {
                // 釋放鎖成功,返回同步狀態
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            // 釋放鎖失敗,狀態置爲CANCELLED
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }
    
    // 釋放完成鎖之後,需要喚醒後繼節點
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    // 獲取鎖
    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);
        }
    }

    // 節點是否在阻塞隊列中
    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);
    }

    // 從阻塞隊列尾部往前找,判斷當前節點是否在阻塞隊列中
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            // 當前節點爲尾節點,在阻塞隊列
            if (t == node)
                return true;
            // 尾節點爲空,不在阻塞隊列
            if (t == null)
                return false;
            // 尾部節點前移
            t = t.prev;
        }
    }

  • signal()方法源碼解讀
    • 將等待隊列中等待時間最長的節點移動到同步隊列中,使得該節點能夠有機會獲得lock。等待隊列是先進先出(FIFO),等待隊列頭節點必然是等待時間最長的節點,每次調用condition的signal方法是將頭節點移動到同步隊列中。
    public final void signal() {
        // 當前線程必須獲取鎖才行
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
            
        // 喚醒等待隊列中的第一個節點
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }

    // 節點從等待隊列移到同步隊列
    private void doSignal(Node first) {
        do {
            // 如果等待隊列值有一個節點
            if ( (firstWaiter = first.nextWaiter) == null)
                // lastWaiter置爲空
                lastWaiter = null;
            
            // first節點從等待隊列中出來
            // 判斷Node從Condition queue轉移到Sync Queue裏是通過signal還是timeout/interrupt
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
           // 調用transferForSignal將first轉移到Sync Queue裏
           // 不成功, 則將firstWaiter重新賦值給first
    }

    // 加入到阻塞隊列
    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
         // 如果無法更新同步狀態,節點狀態爲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;
        // 如果前驅節點狀態爲超時或者中斷,或者CAS設置喚醒狀態失敗
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 喚醒當前線程
            LockSupport.unpark(node.thread);
        return true;
    }

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