国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

線程間的同步與通信(5)——ReentrantLock源碼分析

susheng / 1230人閱讀

摘要:之所以使用這種方式是因?yàn)樵诨謴?fù)一個(gè)被掛起的線程與該線程真正運(yùn)行之間存在著嚴(yán)重的延遲。這樣我們就可以把線程恢復(fù)運(yùn)行的這段時(shí)間給利用起來(lái)了,結(jié)果就是線程更早的獲取了鎖,線程獲取鎖的時(shí)刻也沒(méi)有推遲。

前言

系列文章目錄

上一篇 我們學(xué)習(xí)了lock接口,本篇我們就以ReentrantLock為例,學(xué)習(xí)一下Lock鎖的基本的實(shí)現(xiàn)。我們先來(lái)看看Lock接口中的方法與ReentrantLock對(duì)其實(shí)現(xiàn)的對(duì)照表:

Lock 接口 ReentrantLock 實(shí)現(xiàn)
lock() sync.lock()
lockInterruptibly() sync.acquireInterruptibly(1)
tryLock() sync.nonfairTryAcquire(1)
tryLock(long time, TimeUnit unit) sync.tryAcquireNanos(1, unit.toNanos(timeout))
unlock() sync.release(1)
newCondition() sync.newCondition()

從表中可以看出,ReentrantLock對(duì)于Lock接口的實(shí)現(xiàn)都是直接“轉(zhuǎn)交”給sync對(duì)象的。

核心屬性

ReentrantLock只有一個(gè)sync屬性,別看只有一個(gè)屬性,這個(gè)屬性提供了所有的實(shí)現(xiàn),我們上面介紹ReentrantLock對(duì)Lock接口的實(shí)現(xiàn)的時(shí)候就說(shuō)到,它對(duì)所有的Lock方法的實(shí)現(xiàn)都調(diào)用了sync的方法,這個(gè)sync就是ReentrantLock的屬性,它繼承了AQS.

private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
    abstract void lock();
    //...
}

在Sync類中,定義了一個(gè)抽象方法lock,該方法應(yīng)當(dāng)由繼承它的子類來(lái)實(shí)現(xiàn),關(guān)于繼承它的子類,我們?cè)谙乱还?jié)分析構(gòu)造函數(shù)時(shí)再看。

構(gòu)造函數(shù)

ReentrantLock共有兩個(gè)構(gòu)造函數(shù):

public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

默認(rèn)的構(gòu)造函數(shù)使用了非公平鎖,另外一個(gè)構(gòu)造函數(shù)通過(guò)傳入一個(gè)boolean類型的fair變量來(lái)決定使用公平鎖還是非公平鎖。其中,F(xiàn)airSync和NonfairSync的定義如下:

static final class FairSync extends Sync {
    
    final void lock() {//省略實(shí)現(xiàn)}

    protected final boolean tryAcquire(int acquires) {//省略實(shí)現(xiàn)}
}

static final class NonfairSync extends Sync {
    
    final void lock() {//省略實(shí)現(xiàn)}

    protected final boolean tryAcquire(int acquires) {//省略實(shí)現(xiàn)}
}

這里為什么默認(rèn)創(chuàng)建的是非公平鎖呢?因?yàn)榉枪芥i的效率高呀,當(dāng)一個(gè)線程請(qǐng)求非公平鎖時(shí),如果在發(fā)出請(qǐng)求的同時(shí)該鎖變成可用狀態(tài),那么這個(gè)線程會(huì)跳過(guò)隊(duì)列中所有的等待線程而獲得鎖。有的同學(xué)會(huì)說(shuō)了,這不就是插隊(duì)嗎?
沒(méi)錯(cuò),這就是插隊(duì)!這也就是為什么它被稱作非公平鎖。
之所以使用這種方式是因?yàn)椋?/p>

在恢復(fù)一個(gè)被掛起的線程與該線程真正運(yùn)行之間存在著嚴(yán)重的延遲。

在公平鎖模式下,大家講究先來(lái)后到,如果當(dāng)前線程A在請(qǐng)求鎖,即使現(xiàn)在鎖處于可用狀態(tài),它也得在隊(duì)列的末尾排著,這時(shí)我們需要喚醒排在等待隊(duì)列隊(duì)首的線程H(在AQS中其實(shí)是次頭節(jié)點(diǎn)),由于恢復(fù)一個(gè)被掛起的線程并且讓它真正運(yùn)行起來(lái)需要較長(zhǎng)時(shí)間,那么這段時(shí)間鎖就處于空閑狀態(tài),時(shí)間和資源就白白浪費(fèi)了,非公平鎖的設(shè)計(jì)思想就是將這段白白浪費(fèi)的時(shí)間利用起來(lái)——由于線程A在請(qǐng)求鎖的時(shí)候本身就處于運(yùn)行狀態(tài),因此如果我們此時(shí)把鎖給它,它就會(huì)立即執(zhí)行自己的任務(wù),因此線程A有機(jī)會(huì)在線程H完全喚醒之前獲得、使用以及釋放鎖。這樣我們就可以把線程H恢復(fù)運(yùn)行的這段時(shí)間給利用起來(lái)了,結(jié)果就是線程A更早的獲取了鎖,線程H獲取鎖的時(shí)刻也沒(méi)有推遲。因此提高了吞吐量。

當(dāng)然,非公平鎖僅僅是在當(dāng)前線程請(qǐng)求鎖,并且鎖處于可用狀態(tài)時(shí)有效,當(dāng)請(qǐng)求鎖時(shí),鎖已經(jīng)被其他線程占有時(shí),就只能還是老老實(shí)實(shí)的去排隊(duì)了。

無(wú)論是非公平鎖的實(shí)現(xiàn)NonfairSync還是公平鎖的實(shí)現(xiàn)FairSync,它們都覆寫(xiě)了lock方法和tryAcquire方法,這兩個(gè)方法都將用于獲取一個(gè)鎖。

Lock接口方法實(shí)現(xiàn) lock() 公平鎖實(shí)現(xiàn)

關(guān)于ReentrantLock對(duì)于lock方法的公平鎖的實(shí)現(xiàn)邏輯,我們?cè)谥鹦蟹治鯝QS源碼(1)——獨(dú)占鎖的獲取中已經(jīng)講過(guò)了,這里不再贅述。如果你還沒(méi)有看過(guò)那篇文章或者還不了解AQS,建議先去看一下那一篇文章,然后再讀下文。

非公平鎖實(shí)現(xiàn)

接下來(lái)我們看看非公平鎖的實(shí)現(xiàn)邏輯:

// NonfairSync中的lock方法
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

對(duì)比公平鎖中的lock方法:

// FairSync中的lock方法
final void lock() {
    acquire(1);
}

可見(jiàn),相比公平鎖,非公平鎖在當(dāng)前鎖沒(méi)有被占用時(shí),可以直接嘗試去獲取鎖,而不用排隊(duì),所以它在一開(kāi)始就嘗試使用CAS操作去搶鎖,只有在該操作失敗后,才會(huì)調(diào)用AQS的acquire方法。

由于acquire方法中除了tryAcquire由子類實(shí)現(xiàn)外,其余都由AQS實(shí)現(xiàn),我們?cè)谇懊娴奈恼轮幸呀?jīng)介紹的很詳細(xì)了,這里不再贅述,我們僅僅看一下非公平鎖的tryAcquire方法實(shí)現(xiàn):

// NonfairSync中的tryAcquire方法實(shí)現(xiàn)
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

它調(diào)用了Sync類的nonfairTryAcquire方法:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 只有這一處和公平鎖的實(shí)現(xiàn)不同,其它的完全一樣。
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

我們可以拿它和公平鎖的tryAcquire對(duì)比一下:

// FairSync中的tryAcquire方法實(shí)現(xiàn)
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

看見(jiàn)沒(méi)?這兩個(gè)方法幾乎一模一樣,唯一的區(qū)別就是非公平鎖在搶鎖時(shí)不再需要調(diào)用hasQueuedPredecessors方法先去判斷是否有線程排在自己前面,而是直接爭(zhēng)鎖,其它的完全和公平鎖一致。

lockInterruptibly()

前面的lock方法是阻塞式的,搶到鎖就返回,搶不到鎖就將線程掛起,并且在搶鎖的過(guò)程中是不響應(yīng)中斷的(關(guān)于不響應(yīng)中斷,見(jiàn)這篇文章末尾的分析),lockInterruptibly提供了一種響應(yīng)中斷的方式,在ReentrantLock中,無(wú)論是公平鎖還是非公平鎖,這個(gè)方法的實(shí)現(xiàn)都是一樣的

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

他們都調(diào)用了AQS的acquireInterruptibly方法:

public final void acquireInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

該方法首先檢查當(dāng)前線程是否已經(jīng)被中斷過(guò)了,如果已經(jīng)被中斷了,則立即拋出InterruptedException(這一點(diǎn)是lockInterruptibly要求的,參見(jiàn)上一篇Lock接口的介紹)。

如果調(diào)用這個(gè)方法時(shí),當(dāng)前線程還沒(méi)有被中斷過(guò),則接下來(lái)先嘗試用普通的方法來(lái)獲取鎖(tryAcquire)。如果獲取成功了,則萬(wàn)事大吉,直接就返回了;否則,與前面的lock方法一樣,我們需要將當(dāng)前線程包裝成Node扔進(jìn)等待隊(duì)列,所不同的是,這次,在隊(duì)列中嘗試獲取鎖時(shí),如果發(fā)生了中斷,我們需要對(duì)它做出響應(yīng), 并拋出異常

private void doAcquireInterruptibly(int arg) throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return; //與acquireQueued方法的不同之處
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException(); //與acquireQueued方法的不同之處
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

如果你在上面分析lock方法的時(shí)候已經(jīng)理解了acquireQueued方法,那么再看這個(gè)方法就很輕松了,我們把lock方法中的acquireQueued拿出來(lái)和上面對(duì)比一下:

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
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);
    }
}

通過(guò)代碼對(duì)比可以看出,doAcquireInterruptiblyacquireQueued(addWaiter(Node.EXCLUSIVE), arg))的調(diào)用本質(zhì)上講并無(wú)區(qū)別。只不過(guò)對(duì)于addWaiter(Node.EXCLUSIVE),一個(gè)是外部調(diào)用,通過(guò)參數(shù)傳進(jìn)來(lái);一個(gè)是直接在方法內(nèi)部調(diào)用。所以這兩個(gè)方法的邏輯幾乎是一樣的,唯一的不同就是在doAcquireInterruptibly中,當(dāng)我們檢測(cè)到中斷后,不再是簡(jiǎn)單的記錄中斷狀態(tài),而是直接拋出InterruptedException

當(dāng)拋出中斷異常后,在返回前,我們將進(jìn)入finally代碼塊進(jìn)行善后工作,很明顯,此時(shí)failed是為true的,我們將調(diào)用cancelAcquire方法:

private void cancelAcquire(Node node) {
    // Ignore if node doesn"t exist
    if (node == null)
        return;

    node.thread = null;

    // 由當(dāng)前節(jié)點(diǎn)向前遍歷,跳過(guò)那些已經(jīng)被cancel的節(jié)點(diǎn)
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;
    
    // 從當(dāng)前節(jié)點(diǎn)向前開(kāi)始查找,找到第一個(gè)waitStatus>0的Node, 該節(jié)點(diǎn)為pred
    // predNext即是pred節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
    // 到這里可知,pred節(jié)點(diǎn)是沒(méi)有被cancel的節(jié)點(diǎn),但是pred節(jié)點(diǎn)往后,一直到當(dāng)前節(jié)點(diǎn)Node都處于被Cancel的狀態(tài)
    Node predNext = pred.next;

    //將當(dāng)前節(jié)點(diǎn)的waitStatus的狀態(tài)設(shè)為Node.CANCELLED
    node.waitStatus = Node.CANCELLED;

    // 如果當(dāng)前節(jié)點(diǎn)是尾節(jié)點(diǎn),則將之前找到的節(jié)點(diǎn)pred重新設(shè)置成尾節(jié)點(diǎn),并將pred節(jié)點(diǎn)的next屬性由predNext修改成Null
    // 這一段本質(zhì)上是將pred節(jié)點(diǎn)后面的節(jié)點(diǎn)全部移出隊(duì)列,因?yàn)樗鼈兌急籧ancel掉了
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // 到這里說(shuō)明當(dāng)前節(jié)點(diǎn)已經(jīng)不是尾節(jié)點(diǎn)了,或者設(shè)置新的尾節(jié)點(diǎn)失敗了
        // 我們前面說(shuō)過(guò),并發(fā)條件下,什么都有可能發(fā)生
        // 即在當(dāng)前線程運(yùn)行這段代碼的過(guò)程中,其他線程可能已經(jīng)入隊(duì)了,成為了新的尾節(jié)點(diǎn)
        // 雖然我們之前已經(jīng)將當(dāng)前節(jié)點(diǎn)的waitStatus設(shè)為了CANCELLED 
        // 但是由我們?cè)诜治鰈ock方法的文章可知,新的節(jié)點(diǎn)入隊(duì)后會(huì)設(shè)置鬧鐘,將找一個(gè)沒(méi)有CANCEL的前驅(qū)節(jié)點(diǎn),將它的status設(shè)置成SIGNAL以喚醒自己。
        // 所以,在當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)入隊(duì)后,可能將當(dāng)前節(jié)點(diǎn)的waitStatus修改成了SIGNAL
        // 而在這時(shí),我們發(fā)起了中斷,又將這個(gè)waitStatus修改成CANCELLED
        // 所以在當(dāng)前節(jié)點(diǎn)出隊(duì)前,要負(fù)責(zé)喚醒后繼節(jié)點(diǎn)。
        int ws;
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

這個(gè)cancelAcquire方法不僅是取消了當(dāng)前節(jié)點(diǎn)的排隊(duì),還會(huì)同時(shí)將當(dāng)前節(jié)點(diǎn)之前的那些已經(jīng)CANCEL掉的節(jié)點(diǎn)移出隊(duì)列。不過(guò)這里尤其需要注意的是,這里是在并發(fā)條件下,此時(shí)此刻,新的節(jié)點(diǎn)可能已經(jīng)入隊(duì)了,成為了新的尾節(jié)點(diǎn),這將會(huì)導(dǎo)致node == tail && compareAndSetTail(node, pred)這一條件失敗。

這個(gè)函數(shù)的前半部分是就是基于當(dāng)前節(jié)點(diǎn)就是隊(duì)列的尾節(jié)點(diǎn)的,即在執(zhí)行這個(gè)函數(shù)時(shí),沒(méi)有新的節(jié)點(diǎn)入隊(duì),這部分的邏輯比較簡(jiǎn)單,大家直接看代碼中的注釋解釋即可。

而后半部分是基于有新的節(jié)點(diǎn)加進(jìn)來(lái),當(dāng)前節(jié)點(diǎn)已經(jīng)不再是尾節(jié)點(diǎn)的情況,我們?cè)敿?xì)看看這else部分:

if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
} else {
    int ws;
    if (pred != head &&
        ((ws = pred.waitStatus) == Node.SIGNAL ||
         (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
        pred.thread != null) {
        Node next = node.next;
        if (next != null && next.waitStatus <= 0)
            compareAndSetNext(pred, predNext, next); //將pred節(jié)點(diǎn)的后繼節(jié)點(diǎn)改為當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)
    } else {
        unparkSuccessor(node);
    }

    node.next = node; // help GC
}

(這里再說(shuō)明一下pred變量所代表的含義:它表示了從當(dāng)前節(jié)點(diǎn)向前遍歷所找到的第一個(gè)沒(méi)有被cancel的節(jié)點(diǎn)。)

執(zhí)行到else代碼塊,則我們目前的狀況如下:

當(dāng)前線程被中斷了,我們已經(jīng)將它的Node的waitStatus屬性設(shè)為CANCELLED,thread屬性置為null

在執(zhí)行這個(gè)方法期間,又有其他線程加入到隊(duì)列中來(lái),成為了新的尾節(jié)點(diǎn),使得當(dāng)前線程已經(jīng)不是隊(duì)尾了

在這種情況下,我們將執(zhí)行if語(yǔ)句,將pred節(jié)點(diǎn)的后繼節(jié)點(diǎn)改為當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)(compareAndSetNext(pred, predNext, next)),即將從pred節(jié)點(diǎn)開(kāi)始(不包含pred節(jié)點(diǎn))一直到當(dāng)前節(jié)點(diǎn)(包括當(dāng)前節(jié)點(diǎn))之間的所有節(jié)點(diǎn)全部移出隊(duì)列,因?yàn)樗麄兌际潜籧ancel的節(jié)點(diǎn)。當(dāng)然這是基于一定條件的,條件為:

pred節(jié)點(diǎn)不是頭節(jié)點(diǎn)

pred節(jié)點(diǎn)的thread不為null

pred節(jié)點(diǎn)的waitStatus屬性是SIGNAL或者是小于等于0但是被我們成功的設(shè)置成signal

上面這三個(gè)條件保證了pred節(jié)點(diǎn)確實(shí)是一個(gè)正在正常等待鎖的線程,并且它的waitStatus屬性為SIGNAL。
如果這一條件無(wú)法被滿足,那么我們將直接通過(guò)unparkSuccessor喚醒它的后繼節(jié)點(diǎn)。

到這里,我們總結(jié)一下cancelAcquire方法:

如果要cancel的節(jié)點(diǎn)已經(jīng)是尾節(jié)點(diǎn)了,則在我們后面并沒(méi)有節(jié)點(diǎn)需要喚醒,我們只需要從當(dāng)前節(jié)點(diǎn)(即尾節(jié)點(diǎn))開(kāi)始向前遍歷,找到所有已經(jīng)cancel的節(jié)點(diǎn),將他們移出隊(duì)列即可

如果要cancel的節(jié)點(diǎn)后面還有別的節(jié)點(diǎn),并且我們找到的pred節(jié)點(diǎn)處于正常等待狀態(tài),我們還是直接將從當(dāng)前節(jié)點(diǎn)開(kāi)始,到pred節(jié)點(diǎn)直接的所有節(jié)點(diǎn),全部移出隊(duì)列,這里并不需要喚醒當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn),因?yàn)樗呀?jīng)接在了pred的后面,pred的waitStatus已經(jīng)被置為SIGNAL,它會(huì)負(fù)責(zé)喚醒后繼節(jié)點(diǎn)

如果上面的條件不滿足,按說(shuō)明當(dāng)前節(jié)點(diǎn)往前已經(jīng)沒(méi)有在等待中的線程了,我們就直接將后繼節(jié)點(diǎn)喚醒。

有的同學(xué)就要問(wèn)了,那第3條只是把當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)喚醒了,并沒(méi)有將當(dāng)前節(jié)點(diǎn)移除隊(duì)列呀?但是當(dāng)前節(jié)點(diǎn)已經(jīng)取消排隊(duì)了,不是應(yīng)該移除隊(duì)列嗎?
別著急,在后繼節(jié)點(diǎn)被喚醒后,它會(huì)在搶鎖時(shí)調(diào)用的shouldParkAfterFailedAcquire方法里面跳過(guò)已經(jīng)CANCEL的節(jié)點(diǎn),那個(gè)時(shí)候,當(dāng)前節(jié)點(diǎn)就會(huì)被移出隊(duì)列了。

tryLock()

由于tryLock僅僅是用于檢查鎖在當(dāng)前調(diào)用的時(shí)候是不是可獲得的,所以即使現(xiàn)在使用的是非公平鎖,在調(diào)用這個(gè)方法時(shí),當(dāng)前線程也會(huì)直接嘗試去獲取鎖,哪怕這個(gè)時(shí)候隊(duì)列中還有在等待中的線程。所以這一方法對(duì)于公平鎖和非公平鎖的實(shí)現(xiàn)是一樣的,它被定義在Sync類中,由FairSync和NonfairSync直接繼承使用:

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

這個(gè)nonfairTryAcquire我們?cè)谏厦娣治龇枪芥i的lock方法時(shí)已經(jīng)講過(guò)了,這里只是簡(jiǎn)單的方法復(fù)用。該方法不存在任何和隊(duì)列相關(guān)的操作,僅僅就是直接嘗試去獲鎖,成功了就返回true,失敗了就返回false。

可能大家會(huì)覺(jué)得公平鎖也使用這種方式去tryLock就喪失了公平性,但是這種方式在某些情況下是非常有用的,如果你還是想維持公平性,那應(yīng)該使用帶超時(shí)機(jī)制的tryLock

tryLock(long timeout, TimeUnit unit)

與立即返回的tryLock()不同,tryLock(long timeout, TimeUnit unit)帶了超時(shí)時(shí)間,所以是阻塞式的,并且在獲取鎖的過(guò)程中可以響應(yīng)中斷異常:

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
}

lockInterruptibly方法一樣,該方法首先檢查當(dāng)前線程是否已經(jīng)被中斷過(guò)了,如果已經(jīng)被中斷了,則立即拋出InterruptedException

隨后我們通過(guò)調(diào)用tryAcquiredoAcquireNanos(arg, nanosTimeout)方法來(lái)嘗試獲取鎖,注意,這時(shí)公平鎖和非公平鎖對(duì)于tryAcquire方法就有不同的實(shí)現(xiàn)了,公平鎖首先會(huì)檢查當(dāng)前有沒(méi)有別的線程在隊(duì)列中排隊(duì),關(guān)于公平鎖和非公平鎖對(duì)tryAcquire的不同實(shí)現(xiàn)上文已經(jīng)講過(guò)了,這里不再贅述。我們直接來(lái)看doAcquireNanos,這個(gè)方法其實(shí)和前面說(shuō)的doAcquireInterruptibly方法很像,我們通過(guò)將相同的部分注釋掉,直接看不同的部分:

private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    /*final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;*/
                return true; // doAcquireInterruptibly中為 return
            /*}*/
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
       /* }
    } finally {
        if (failed)
            cancelAcquire(node);
    }*/
}

可以看出,這兩個(gè)方法的邏輯大差不差,只是doAcquireNanos多了對(duì)于截止時(shí)間的檢查。

不過(guò)這里有兩點(diǎn)需要注意,一個(gè)是doAcquireInterruptibly是沒(méi)有返回值的,而doAcquireNanos是有返回值的。這是因?yàn)?b>doAcquireNanos有可能因?yàn)楂@取到鎖而返回,也有可能因?yàn)槌瑫r(shí)時(shí)間到了而返回,為了區(qū)分這兩種情況,因?yàn)槌瑫r(shí)時(shí)間而返回時(shí),我們將返回false,代表并沒(méi)有獲取到鎖。

另外一點(diǎn)值得注意的是,上面有一個(gè)nanosTimeout > spinForTimeoutThreshold的條件,在它滿足的時(shí)候才會(huì)將當(dāng)前線程掛起指定的時(shí)間,這個(gè)spinForTimeoutThreshold是個(gè)啥呢:

/**
 * The number of nanoseconds for which it is faster to spin
 * rather than to use timed park. A rough estimate suffices
 * to improve responsiveness with very short timeouts.
 */
static final long spinForTimeoutThreshold = 1000L;

它就是個(gè)閾值,是為了提升性能用的。如果當(dāng)前剩下的等待時(shí)間已經(jīng)很短了,我們就直接使用自旋的形式等待,而不是將線程掛起,可見(jiàn)作者為了盡可能地優(yōu)化AQS鎖的性能費(fèi)足了心思。

unlock()

unlock操作用于釋放當(dāng)前線程所占用的鎖,這一點(diǎn)對(duì)于公平鎖和非公平鎖的實(shí)現(xiàn)是一樣的,所以該方法被定義在Sync類中,由FairSync和NonfairSync直接繼承使用:

public void unlock() {
    sync.release(1);
}

關(guān)于ReentrantLock的釋放鎖的操作,我們?cè)谥鹦蟹治鯝QS源碼(2)——獨(dú)占鎖的釋放中已經(jīng)詳細(xì)的介紹過(guò)了,這里就不再贅述了。

newCondition()

ReentrantLock本身并沒(méi)有實(shí)現(xiàn)Condition方法,它是直接調(diào)用了AQS的newCondition方法

public Condition newCondition() {
    return sync.newCondition();
}

而AQS的newCondtion方法就是簡(jiǎn)單地創(chuàng)建了一個(gè)ConditionObject對(duì)象:

final ConditionObject newCondition() {
    return new ConditionObject();
}

關(guān)于ConditionObject對(duì)象的源碼分析,請(qǐng)參見(jiàn) 逐行分析AQS源碼(4)——Condition接口實(shí)現(xiàn)

總結(jié)

ReentrantLock對(duì)于Lock接口方法的實(shí)現(xiàn)大多數(shù)是直接調(diào)用了AQS的方法,AQS中已經(jīng)完成了大多數(shù)邏輯的實(shí)現(xiàn),子類只需要直接繼承使用即可,這足見(jiàn)AQS在并發(fā)編程中的地位。當(dāng)然,有一些邏輯還是需要ReentrantLock自己去實(shí)現(xiàn)的,例如tryAcquire的邏輯。

AQS在并發(fā)編程中的地位舉足輕重,只要弄懂了它,我們?cè)趯W(xué)習(xí)其他并發(fā)編程工具的時(shí)候就會(huì)容易很多。

(完)

系列文章目錄

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77237.html

相關(guān)文章

  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫(xiě)博客的,寫(xiě)作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷?xiě)作的時(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫(xiě)出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    lijy91 評(píng)論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫(xiě)博客的,寫(xiě)作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷?xiě)作的時(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫(xiě)出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    Yumenokanata 評(píng)論0 收藏0
  • 線程間的同步通信(7)——CyclicBarrier源碼分析

    摘要:例如,線程需要互相等待,保證所有線程都執(zhí)行完了之后才能一起通過(guò)。獲取正在等待中的線程數(shù)注意,這里加了鎖,因?yàn)榉椒赡軙?huì)被多個(gè)線程同時(shí)修改。只要有一行沒(méi)有處理完,所有的線程都會(huì)在處等待,最后一個(gè)執(zhí)行完的線程將會(huì)負(fù)責(zé)喚醒所有等待的線程 前言 系列文章目錄 上一篇 我們學(xué)習(xí)了基于AQS共享鎖實(shí)現(xiàn)的CountDownLatch,本篇我們來(lái)看看另一個(gè)和它比較像的并發(fā)工具CyclicBarrier...

    freewolf 評(píng)論0 收藏0
  • Java 多線程并發(fā)編程面試筆錄一覽

    摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程。恢復(fù)線程,已過(guò)時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識(shí)體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...

    bitkylin 評(píng)論0 收藏0
  • 深入理解 Java 多線程系列(1)——一個(gè)簡(jiǎn)單需求的并行改造 & Java多線程通信問(wèn)題

    摘要:所以接下來(lái),我們需要簡(jiǎn)單的介紹下多線程中的并發(fā)通信模型。比如中,以及各種鎖機(jī)制,均為了解決線程間公共狀態(tài)的串行訪問(wèn)問(wèn)題。 并發(fā)的學(xué)習(xí)門檻較高,相較單純的羅列并發(fā)編程 API 的枯燥被動(dòng)學(xué)習(xí)方式,本系列文章試圖用一個(gè)簡(jiǎn)單的栗子,一步步結(jié)合并發(fā)編程的相關(guān)知識(shí)分析舊有實(shí)現(xiàn)的不足,再實(shí)現(xiàn)邏輯進(jìn)行分析改進(jìn),試圖展示例子背后的并發(fā)工具與實(shí)現(xiàn)原理。 本文是本系列的第一篇文章,提出了一個(gè)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景...

    ruicbAndroid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<