【Java並發編程實戰】----- AQS(二):獲取鎖、釋放鎖


上篇博客稍微介紹了一下AQS,下面我們來關注下AQS的所獲取和鎖釋放。

AQS鎖獲取

AQS包含如下幾個方法:

acquire(int arg):以獨占模式獲取對象,忽略中斷。

acquireInterruptibly(int arg): 以獨占模式獲取對象,如果被中斷則中止。

acquireShared(int arg): 以共享模式獲取對象,忽略中斷。

acquireSharedInterruptibly(int arg)以共享模式獲取對象,如果被中斷則中止。

tryAcquire(int arg):試圖在獨占模式下獲取對象狀態。

tryAcquireNanos(int arg, long nanosTimeout):試圖以獨占模式獲取對象,如果被中斷則中止,如果到了給定超時時間,則會失敗。

tryAcquireShared(int arg):試圖在共享模式下獲取對象狀態。

tryAcquireSharedNanos(int arg, long nanosTimeout):試圖以共享模式獲取對象,如果被中斷則中止,如果到了給定超時時間,則會失敗。

對於lock.lock()最終都會調用AQS的acquire()方法,Semaphore.acquire()最終會調用AQS的acquireSharedInterruptibly()方法,其中acquire()源代碼如下:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire:去嘗試獲取鎖,獲取成功則設置鎖狀態並返回true,否則返回false。

addWaiter:將當前線程加入到CLH隊列隊尾。

acquireQueued:當前線程會根據公平性原則來進行阻塞等待,直到獲取鎖為止;並且返回當前線程在等待過程中有沒有中斷過。

selfInterrupt:產生一個中斷。

其主要流程如下:

2015120400001

1、首先線程嘗試獲取鎖,如果成功則直接返回,不成功則新建一個Node節點並添加到CLH隊列中。tryAcquire嘗試獲取鎖,addWaiter則新建節點並添加到CLH隊列中。其中tryAcquire,AQS並沒有提供實現,它僅僅只是拋出一個異常,具體的實現需要各個鎖自己實現。

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

addWaiter后面講述。

2、acquireQueued主要功能是根據該節點尋找CLH隊列的頭結點,並且嘗試獲取鎖,判斷是否需要掛起,並且返回掛起標識。如下:

final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }

在acquireQueued()內部仍然調用tryAcquire()來獲取鎖。更多詳情請參考:【Java並發編程實戰】—–“J.U.C”:ReentrantLock之二lock方法分析

selfInterrupt:產生一個中斷。如果在acquireQueued()中當前線程被中斷過,則需要產生一個中斷。

private static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

AQS鎖釋放

AQS釋放鎖的方法主要有:

release(int arg):以獨占模式釋放對象。

releaseShared(int arg): 以共享模式釋放對象

tryRelease(int arg):試圖設置狀態來反映獨占模式下的一個釋放。

tryReleaseShared(int arg):試圖設置狀態來反映共享模式下的一個釋放。

釋放鎖相對於獲取鎖來說還是比較簡單的,其主要流程如下:

2015120400002

其代碼如下(release()):

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryeRelease():嘗試釋放鎖,AQS也同樣沒有提供實現,具體實現方法要其子類自己內部實現,AQS僅僅只是拋出一個異常。

protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

unparkSuccessor:用於喚醒節點。更多,請參考:【Java並發編程實戰】—–“J.U.C”:ReentrantLock之三unlock方法分析

 

參考文獻:

1、JAVA並發編程學習筆記之AQS源碼分析(獲取與釋放)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM