java鎖(4)隊列同步器AQS詳解

1、AQS簡介

AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是用來構建鎖或者其他同步組件(信號量、事件等)的基礎框架類。JDK中許多併發工具類的內部實現都依賴於AQS,如ReentrantLock, Semaphore, CountDownLatch等等。

AQS的主要使用方式是繼承它作爲一個內部輔助類實現同步原語,它可以簡化你的併發工具的內部實現,屏蔽同步狀態管理、線程的排隊、等待與喚醒等底層操作。

在基於AQS構建的同步器類中,最基本的操作包括各種形式的獲取操作和釋放操作。獲取操作是一種依賴狀態的操作,並且通常會阻塞。當使用鎖或信號量時,“獲取”操作的含義就很直觀,即獲取的是鎖或者許可,並且調用者可能會一直等待直到同步器類處於可被獲取的狀態。如果一個類想成爲狀態依賴的類,那麼它必須擁有一些狀態。AQS負責管理同步器類中的狀態,它管理了一個整數狀態信息,可以通過getstate,setState以及compareAndSetState等 protected類型方法來進行操作。這個整數可以用於表示任意狀態。

AQS主要做三件事:

  • 同步狀態的管理
  • 線程的阻塞和喚醒
  • 同步隊列的維護

狀態管理API:

  • int getState(): 獲取同步狀態
  • void setState(): 設置同步狀態
  • boolean compareAndSetState(int expect, int update):基於CAS,原子設置狀態

AQS模板方法:

方法 描述
void acquire(int arg) 獨佔式獲取同步狀態,如果當前線程獲取同步狀態成功,則由該方法返回,否則,將會進入同步隊列等待,該方法將會調用重寫的tryAcquire(intarg)方法
void acquireInterruptibly(int arg) 與acquire(int arg)相同,但是該方法響應中斷,當前線程未獲取到同步狀態而進入同步隊列中,如果當前線程被中斷,則該方法會拋出InterruptedException並返回
boolean tryAcquireNanos(int arg,long nanos) 在acquireInterruptibly(int arg)基礎上增加了超時限制,如果當前線程在超時時間內沒有獲取到同步狀態,那麼就會返回false,如果獲取到就返回true
void acquireShared(int arg) 共享式的獲取同步狀態,如果當前線程未獲取到同步狀態,將會進入同步隊列等待,與獨佔式獲取的主要區別是在同一時刻可以有多個線程獲取到同步狀態。
void acquireSharedInterruptibly(int arg) 與acquireShared(int arg)相同,該方法響應中斷。
boolean tryAcquireSharedNanos(int arg,long nanos) 在acquireSharedInterruptibly(int arg)基礎上增加了超時限制。
boolean release(int arg) 獨佔式的釋放同步狀態,該方法會在釋放同步狀態之後,將同步隊列中第一個節點包含的線程喚醒。
boolean releaseShared(int arg) 共享式的釋放同步狀態
Collection getQueuedThreads() 獲取等待在同步隊列上的線程集合

同步器可重寫的方法:

方法 描述
boolean tryAcquire(int arg) 獨佔獲取同步狀態,實現該方法需要查詢當前狀態,並判斷同步狀態是否符合預期狀態,然後再進行CAS設置同步狀態。
boolean tryRelease(int arg) 獨佔式釋放同步狀態,等待獲取同步狀態的線程將有機會獲取同步狀態
int tryAcquireShared(int arg) 共享式獲取同步狀態,返回大於等於0的值,表示獲取成功,反之失敗
boolean tryReleaseShared(int arg) 共享式釋放同步狀態
boolean isHeldExclusively() 當前同步器是否在獨佔模式下被線程佔用,一般該方法表示是否被當前線程所獨佔

AQS對外暴露的方法:

2、AQS數據結構

因爲獲取鎖是有條件的,沒有獲取鎖的線程就要阻塞等待,那麼就要存儲這些等待的線程。在AQS中使用CLH隊列儲存這些等待的線程,但它並不是直接儲存線程,而是儲存擁有線程的node節點。

2.1、Node數據結構:

static final class Node {
    //共享模式的標記,標識一個節點在共享模式下等待
    static final Node SHARED = new Node();
    //獨佔模式的標記,標識一個節點在獨佔模式下等待
    static final Node EXCLUSIVE = null;

    // waitStatus變量的值,標誌着線程被取消,後續將不會獲取到鎖
    static final int CANCELLED = 1;
     // waitStatus變量的值,標誌着後繼線程(即隊列中此節點之後的節點)需要被阻塞.(用於獨佔鎖)
    static final int SIGNAL = -1;
    // waitStatus變量的值,標誌着線程在Condition條件上等待阻塞.(用於Condition的await等待)
    static final int CONDITION = -2;
    // waitStatus變量的值,標誌着下一個acquireShared方法線程應該被無條件傳播。(用於共享鎖)
    static final int PROPAGATE = -3;

     // 標記着當前節點的狀態,默認狀態是0, 小於0的狀態都是有特殊作用,大於0的狀態表示已取消
     //SIGNAL:此節點的繼任節點被阻塞,故當此節點釋放鎖或中斷時需要換繼任節點的線程。
     //CANCELLED:當獲取鎖超時或被中斷時節點狀態會被設置成此狀態,此狀態的節點不會被再次阻塞;
     //CONDITION:標識此節點在條件隊列中,在同步隊列的節點不會出現此狀態,
     //當節點從條件隊列移到同步隊列時此狀態會被設置爲0;
     //PROPAGATE:一個releaseShared應該被傳播到其他節點,此狀態在doReleaseShared()中調用,
     //以確保傳播傳播在其他插入時保持繼續。
     //總結:狀態小於0表示節點無需被通知喚醒;狀態爲0表示普通同步節點;CONDITION表示節點在
     //等待隊列中,狀態通過CAS進行原子更新。
    volatile int waitStatus;

    /**
     * 前驅節點,在enqueue時設置,在dequeue或前驅節點取消時清除;
     */
    volatile Node prev;

    /**
     * 後繼節點,在enqueue時設置,在dequeue或前驅節點取消時清除;
     * 入隊操作不會設置前驅節點的後繼節點,直到節點連接到隊列;
     * 故next節點爲null不一定表示此節點爲隊列尾部,當next節點爲null時,
     * 可遍歷prev節點進行雙重檢查;已經取消的節點的next指向自己而不是null
     */
    volatile Node next;

    //該節點擁有的線程
    volatile Thread thread;

    /**
     * 1、值爲null或非SHARED;爲null時表示獨佔模式;非SHARED時表示在Condition中等待的隊列;
     * 2、值爲SHARED,表示共享模式;
     */
    Node nextWaiter;

    //是否爲共享模式
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    //當前節點的前驅節點,如果前驅節點爲null,則拋出NPE異常
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // Used to establish initial head or SHARED marker
    }

    //用於在addWaiter()中使用,創建同步隊列中的節點
    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    //在Condition中使用,創建等待隊列的節點
    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

說明:

  • waitStatus:表示當前節點的狀態,會有如下五中狀態:

CANCELLED(1):當獲取鎖超時或被中斷時節點狀態會被設置成此狀態,此狀態的 節點會被unpark,不會參與鎖的獲取,不會被再次阻塞;

0:表示普通節點,當節點初始插入到同步隊列時的狀態;

SIGNAL(-1):此節點的繼任節點被阻塞,故當此節點釋放鎖或中斷時需要換繼任節點的線程。

CONDITION(-2):標識此節點在條件隊列中,在同步隊列的節點不會出現此狀態,當節點從條件隊列移到同步隊列時此狀態會被設置爲0;

PROPAGATE(-3):一個releaseShared應該被傳播到其他節點,此狀態在doReleaseShared()中調用,以確保傳播傳播在其他插入時保持繼續。

總結:狀態小於0表示節點無需被通知喚醒;狀態爲0表示普通同步節點;CONDITION表示節點在等待隊列中,狀態通過CAS進行原子更新。

  • prev:前驅節點,在enqueue時設置,在dequeue或前驅節點取消時清除;
  • next:後繼節點,在enqueue時設置,在dequeue或前驅節點取消時清除; 入隊操作不會設置前驅節點的後繼節點,直到節點連接到隊列; 故next節點爲null不一定表示此節點爲隊列尾部,當next節點爲null時, 可遍歷prev節點進行雙重檢查;已經取消的節點的next指向自己而不是null
  • **nextWaiter: **值爲SHARED,表示共享模式;值爲null或非SHARED,爲null時表示獨佔模式,非SHARED時表示在Condition隊列中等待的節點;

waitStatus狀態狀態:

2.2、AQS數據結構

/**
 * 等待隊列的頭節點,通過setHead方法進行更新;頭結點的狀態不可能是CANCELLED
 */
private transient volatile Node head;

/**
 * 等待隊列的尾節點
 */
private transient volatile Node tail;

/**
 * 同步狀態
 */
private volatile int state;

3、CLH隊列相關操作

3.1、相關屬性的CAS操作

/**
 * 通過CAS設置AQS的head值
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
 * 通過CAS設置AQS的tail值
 */
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

/**
 * 通過CAS設置Node節點的waitStatue值
 */
private static final boolean compareAndSetWaitStatus(Node node,
                                                     int expect,
                                                     int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,
            expect, update);
}

/**
 * 通過CAS設置Node節點的next值
 */
private static final boolean compareAndSetNext(Node node,
                                               Node expect,
                                               Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

3.2、將節點添加到CLH隊尾

//通過空轉及CAS方式,將一個node插入同步隊列的隊尾
private Node enq(final Node node) {
    for (; ; ) {
        Node t = tail;
        
        //尾節點爲空,則直接CAS初始化頭結點,並將尾節點設置爲頭節點
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            //尾節點不爲空,則CAS設置當前節點爲尾節點
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

3.3、將當前線程添加到CLH隊尾

//創建一個給定模式的節點,此節點線程爲當前線程,並將節點加入隊尾
private Node addWaiter(Node mode) {
    //創建線程
    Node node = new Node(Thread.currentThread(), mode);
    //快速檢測尾節點不爲空,則CAS將當前節點替換爲尾節點
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //快速替換失敗?則通過enq()的空轉方式將當前節點插入隊尾
    enq(node);
    return node;
}

4、獨佔鎖

獨佔鎖主要包括兩方面功能:

  • 獲取鎖的功能:既當多個線程一起獲取鎖的時候,只有一個線程能獲取到鎖,其他線程必須在當前位置阻塞等待。
  • 釋放鎖的功能:獲取鎖的線程釋放鎖資源,而且還必須能喚醒正在等待鎖資源的一個線程。

4.1、獨佔鎖獲取流程

4.2、獲取獨佔鎖相關方法

//獲取獨佔鎖,忽略中斷;直到成功獲取鎖,此方法經常被lock.lock調用
public final void acquire(int arg) {
    //tryAcquire:先CAS嘗試獲取鎖,當返回true表示獲取成功,此方法由子類實現;
    //當返回false,則調用
    //acquireQueued進行獲取及入隊阻塞處理,當有其他線程釋放鎖會喚醒改線程。
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//調用tryAcquire嘗試獲取鎖,當獲取失敗就將線程節點入隊並阻塞節點線程;
//直到線程被中斷或被喚醒,會再次嘗試獲取鎖;
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);
    }
}
//判斷當前節點線程是否需要阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //前驅節點的狀態
    int ws = pred.waitStatus;
    
    //前驅節點狀態爲SIGNAL,表示前驅節點在等待獲取鎖的信號
    //故本節點可以安全的阻塞
    if (ws == Node.SIGNAL)
        return true;
    
    //前驅節點waitStatus>0,即爲waitStatus=CANCELLED;
    //表示前驅節點已經被取消,需需要前向遍歷前驅節點,直到狀態
    //不爲CANCELLED的節點,並將此節點設爲node節點的前驅節點;
    //返回false,讓上層調用繼續嘗試獲取鎖
    if (ws > 0) {
        //循環遍歷前驅節點,尋找狀態不爲CANCELLED的節點,並設爲當前節點的
        //前驅節點
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //當前驅節點狀態爲0或PROPAGATE時,通過CAS設置前驅節點狀態爲SIGNAL
        //並返回fase,等待下個循環阻塞當前節點線程;
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

//通過LockSupport.park()阻塞當前線程,直到線程被unpark或被中斷
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

4.3、釋放獨佔鎖相關方法

//釋放獨佔鎖
public final boolean release(int arg) {
    //調用tryRelease方式通過CAS嘗試釋放鎖,tryRelease由子類實現
    if (tryRelease(arg)) {
        Node h = head;
        
        //頭結點不爲空且頭節點狀態不爲0,應該爲SIGNAL
        //表示隊列中有需要喚醒的節點,調用unparkSuccessor進行頭節點線程
        //喚醒操作
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//喚醒節點線程處理
private void unparkSuccessor(Node node) {
    //獲取當前節點狀態
    int ws = node.waitStatus;
    //狀態小於零,則將狀態重置爲0,表示節點處理已經完成
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    //獲取後繼節點,當後繼節點爲空或後繼節點狀態爲CANCELLED時;
    //由tail前向遍歷隊列,找到當前節點的下個有效節點,即waitStatus <= 0
    //的節點
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    //下個節點非空,表示爲等待信號的節點,執行unpark喚醒節點線程
    if (s != null)
        LockSupport.unpark(s.thread);
}

5、共享鎖

5.1、共享鎖獲取流程

5.2、共享鎖獲取相關方法

//獲取共享鎖,忽略中斷;
public final void acquireShared(int arg) {
    //子類實現,CAS方式獲取共享鎖,若獲取失敗,調用doAcquireShared繼續獲取共享鎖
    if (tryAcquireShared(arg) < 0)
        //嘗試獲取共享鎖,獲取失敗則將當前線程節點入隊,直到被通知或被中斷
        doAcquireShared(arg);
}
//獲取共享鎖,若CAS獲取失敗,則將當前節點入隊並阻塞當前線程,直到獲取鎖
private void doAcquireShared(int arg) {
    //將當前節點插入隊尾
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (; ; ) {
            //獲取當前節點的前驅節點,若前驅節點爲頭節點,則嘗試獲取鎖;
            //若獲取失敗,則檢查節點狀態;當節點狀態爲SIGNAL時將節點線程阻塞
            final Node p = node.predecessor();
            if (p == head) {
                //CAS獲取鎖
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    //成功則設置當前節點爲頭節點並將其他節點狀態設爲PROPAGAE
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            //檢查當前節點是否應該阻塞,是則進行阻塞處理,直到被中斷
            if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        //若獲取鎖失敗,則進行取消獲取處理
        if (failed)
            cancelAcquire(node);
    }
}
//設置當前節點爲頭節點並喚醒共享模式下的線程
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; 
    //設爲頭節點
    setHead(node);
    //若propagate > 0或頭結點爲空且頭節點狀態爲 < 0 
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        //若頭節點的後繼節點爲共享模式,則獲取頭結點
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

5.3、共享鎖釋放相關方法

//釋放共享鎖
public final boolean releaseShared(int arg) {
    //cas方式釋放鎖,若失敗則doReleaseShared釋放鎖
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
private void doReleaseShared() {
    for (; ; ) {
        //獲取頭結點,頭結點不爲空且狀態爲SIGNAL時,CAS設置狀態爲0並喚醒線程
        //否則將頭結點狀態設置爲PROPAGATE,然後循環檢查頭結點狀態並試圖喚醒
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            } else if (ws == 0 &&
                    !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

6、condition條件

6.1、condition的實現

首先內部存在一個Condition隊列,存儲着所有在此Condition條件等待的線程。

await系列方法:讓當前持有鎖的線程釋放鎖,並喚醒一個在CLH隊列上等待鎖的線程,再爲當前線程創建一個node節點,插入到Condition隊列(注意不是插入到CLH隊列中)

signal系列方法:其實這裏沒有喚醒任何線程,而是將Condition隊列上的等待節點插入到CLH隊列中,所以當持有鎖的線程執行完畢釋放鎖時,就會喚醒CLH隊列中的一個線程,這個時候纔會喚醒線程。

6.2、await及signal處理流程

6.3、await相關方法

//讓當前持有鎖的線程阻塞等待,並釋放鎖。如果有中斷請求,則拋出InterruptedException異常
public final void await() throws InterruptedException {
    //若當前線程已被中斷,則拋出中斷異常
    if (Thread.interrupted())
        throw new InterruptedException();
   // 爲當前線程創建新的Node節點,並且將這個節點插入到Condition隊列中
    Node node = addConditionWaiter();
    //釋放當前線程持有的鎖,並喚醒同步隊列中的頭結點
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    //如果當前節點補足同步隊列中;
    //阻塞當前線程,當前當前線程被signal信號喚醒後,將當前節點加入同步隊列中;
    //等待獲取獲取鎖
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        //檢查是否被中斷併入隊等待鎖
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 如果節點node已經在同步隊列中了,獲取同步鎖,只有得到鎖才能繼續執行,否則線程繼續阻塞等待
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    // 清除Condition隊列中狀態不是Node.CONDITION的節點    
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    // 是否要拋出異常,或者發出中斷請求
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
//爲當前線程創建新的Node節點,並且將這個節點插入到Condition隊列中
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 如果等待隊列尾節點狀態不是CONDITION,則進行清除操作;
    // 清除隊列中狀態不是CONDITION的節點
    if (t != null && t.waitStatus != Node.CONDITION) {
        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;
}
//從頭到尾部遍歷等待隊列,去除狀態不是CONDITION的節點
private void unlinkCancelledWaiters() {
    //記錄下一個待處理的節點
    Node t = firstWaiter;
    //記錄上一個狀態爲CONDITION的節點
    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;
    }
}
//釋放當前線程佔有的鎖,並喚醒同步隊列一個等待線程
//如果失敗就拋出異常,設置node節點的狀態是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;
    }
}
//判斷節點釋放在同步隊列中
final boolean isOnSyncQueue(Node node) {
    // 如果node的狀態是Node.CONDITION,或者node沒有前一個節點prev,
    // 那麼返回false,節點node不在同步隊列中
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    //如果node有下個節點,則其一定在同步隊列中    
    if (node.next != null) // If has successor, it must be on queue
        return true;
    //從同步隊列中查找node節點
    return findNodeFromTail(node);
}

//根據當前的模式,判斷是否拋出異常或重新中斷等
private void reportInterruptAfterWait(int interruptMode)
        throws InterruptedException {
    if (interruptMode == THROW_IE)
        throw new InterruptedException();
    else if (interruptMode == REINTERRUPT)
        selfInterrupt();
}

6.4、signal相關方法

//如果等待隊列不爲空,則將隊列頭節點插入同步隊列中
public final void signal() {
    //如果當前線程不是獨佔鎖,則拋出異常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    //將等待隊列中的頭結點插入同步隊列中
    if (first != null)
        doSignal(first);
}
//將等待隊列總的頭結點插入同步隊列中
private void doSignal(Node first) {
    do {
        // 原先的Condition隊列頭節點取消,所以重新賦值Condition隊列頭節點
        // 如果新的Condition隊列頭節點爲null,表示Condition隊列爲空了
        // ,所以也要設置Condition隊列尾lastWaiter爲null
        if ((firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
            (first = firstWaiter) != null);
}
// 返回true表示節點node插入到同步隊列中,返回false表示節點node沒有插入到同步隊列中
final boolean transferForSignal(Node node) {
    //如果無法將節點狀態由CONDITION修改爲0,表示節點已在同步隊列中,直接返回false
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    //將節點node插入到同步隊列中,p是原先同步隊列尾節點,也是node節點的前一個節點
    Node p = enq(node);
    int ws = p.waitStatus;
    // 如果前一個節點是已取消狀態,或者不能將它設置成Node.SIGNAL狀態。
    // 就說明節點p之後也不會發起喚醒下一個node節點線程的操作,
    // 所以這裏直接調用 LockSupport.unpark(node.thread)方法,喚醒節點node所在線程
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
//喚醒所有節點
public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}
//循環喚醒所有等待中的節點
private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章