源碼|併發一枝花之ReentrantLock與AQS(2):lockInterruptibly

上次分析了ReentrantLock#lock()與ReentrantLock#unlock()的實現原理,並初步討論了AQS的等待隊列模型,參考源碼|併發一枝花之ReentrantLock與AQS(1):lock、unlockjava

本文繼續分析ReentrantLock#lockInterruptibly(),內容簡單,能夠跳過。node

JDK版本:oracle java 1.8.0_102git

接口聲明

public interface Lock {
    ...
    void lock();

    void lockInterruptibly() throws InterruptedException;

    void unlock();
    ...
}
複製代碼

Lock#lockInterruptibly()是Lock#lock()的一個衍生品,解鎖方法也爲Lock#unlock()。所以,咱們只須要分析Lock#lockInterruptibly()。github

功能

Lock#lockInterruptibly()方法比較特殊,當經過這個方法去獲取鎖時,若是線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態。c#

一個典型用法以下:併發

當兩個線程同時經過Lock#lockInterruptibly()阻塞的獲取某個鎖時,假如此時線程A獲取到了鎖,則線程B只有繼續等待;此時,讓線程A對線程B調用threadB.interrupt()方法,可以中斷線程B的等待,讓線程B能夠先作其餘事。oracle

想對的,若是使用內置鎖synchronized,當一個線程處於等待某個鎖的狀態時,是沒法被中斷的,只有一直等待下去。這是可中斷鎖最大的優點。ui

注意,當一個線程獲取了鎖以後,是不會被Thread#interrupt()方法中斷的。spa

實現原理

基本的實現原理與ReentrantLock#lock()相同。線程

仍以默認的非公平策略爲例進行分析。

lockInterruptibly

public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
複製代碼

回憶ReentrantLock的核心思想:用state表示「全部者線程已經重複獲取該鎖的次數」

acquireInterruptibly

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    public final void acquireInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    ...
}
複製代碼

改寫:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    public final void acquireInterruptibly(int arg) if (Thread.interrupted()) throw new InterruptedException();
        if (tryAcquire(arg)) {
            return;
        }
        doAcquireInterruptibly(arg);
    }
    ...
}
複製代碼

首先,經過tryAcquire()嘗試獲取鎖。若是獲取成功,則直接返回。若是獲取失敗,則藉助doAcquireInterruptibly實現可中斷獲取。

NonfairSync#tryAcquire()在ReentrantLock#lock()中分析過了,直接看AQS#doAcquireInterruptibly()。

doAcquireInterruptibly

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    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;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    ...
}
複製代碼

同AQS#acquireQueued()基本相同,惟一的區別是對中斷信號的處理。

AQS#acquireQueued()被中斷後,將中斷標誌傳給外界,外界再調用Thread#interrupt()復現中斷;而AQS#doAcquireInterruptibly()則直接拋出InterruptedException。

兩者本質上沒什麼不一樣。但AQS#doAcquireInterruptibly()顯示拋出了InterruptedException,調用者必須處理或繼續上拋該異常。

總結

能夠看到,分析完ReentrantLock#lock()後,其衍生品ReentrantLock#lockInterruptibly()的分析也變得極其簡單。ReentrantLock#tryLock()與之類似,再也不分析。


本文連接:源碼|併發一枝花之ReentrantLock與AQS(2):lockInterruptibly
做者:猴子007
出處:monkeysayhi.github.io
本文基於 知識共享署名-相同方式共享 4.0 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。

相關文章
相關標籤/搜索