溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ReentrantLock源碼分析

發(fā)布時(shí)間:2021-11-15 16:58:11 來(lái)源:億速云 閱讀:144 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“ReentrantLock源碼分析”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1.ReentrantLock類(lèi)圖結(jié)構(gòu)

ReentrantLock源碼分析

從類(lèi)圖我們可以直觀地了解到,ReentrantLock最終還是使用AQS來(lái)實(shí)現(xiàn)地,并且根據(jù)參數(shù)來(lái)決定其內(nèi)部是一個(gè)公平????還是非公平鎖????,默認(rèn)是非公平鎖????。

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

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

其中Sync類(lèi)直接繼承自AQS,它的子類(lèi)NonfairSync和FairSync分別實(shí)現(xiàn)了獲取鎖的非公平與公平策略。

如果讀者對(duì)AQS還不了解的話(huà),可以去看看我的這篇文章:抽象同步隊(duì)列AQS——AbstractQueuedSynchronizer鎖詳解

在這里,AQS的state狀態(tài)值表示線(xiàn)程獲取該鎖的可重入次數(shù),在默認(rèn)情況下,state的值為0表示當(dāng)前鎖沒(méi)有被任何線(xiàn)程持有。當(dāng)一個(gè)線(xiàn)程第一次獲取該鎖時(shí),會(huì)嘗試使用CAS設(shè)置state的值為1,

如果CAS成功則當(dāng)前線(xiàn)程獲取了該鎖,然后記錄該鎖的持有者為當(dāng)前線(xiàn)程。在該線(xiàn)程沒(méi)用釋放鎖的情況下第二次獲取該鎖后,狀態(tài)值被設(shè)置為2,這就是可重入次數(shù)。

在該線(xiàn)程釋放鎖時(shí),會(huì)嘗試使用CAS讓狀態(tài)值減1,如果減1后狀態(tài)值為0,則當(dāng)前線(xiàn)程釋放該鎖。

2.獲取鎖的主要方法

2.1 void lock()方法

lock()獲取鎖,其實(shí)就是把state從0變成n(重入鎖可以累加)。實(shí)際調(diào)用的是sync的lock方法,分公平和非公平。

public void lock() {
    sync.lock();
}

在如上代碼中,ReentrantLock的lock()委托給sync類(lèi),根據(jù)創(chuàng)建的ReentrantLock構(gòu)造函數(shù)選擇sync的實(shí)現(xiàn)是NonfairSync還是FairSync,先看看sync的子類(lèi)NonfairSync(非公平鎖????)的情況

final void lock() {
    if (compareAndSetState(0, 1))//CAS設(shè)置狀態(tài)值為1
        setExclusiveOwnerThread(Thread.currentThread());//設(shè)置該鎖的持有者為當(dāng)前線(xiàn)程
    else //CAS失敗的話(huà)
        acquire(1);//調(diào)用AQS的acquire方法,傳遞參數(shù)為1
}

下面是AQS的acquire的核心源碼

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&//調(diào)用ReentantLock重寫(xiě)tryAcquire方法
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//tryAcquire返回false會(huì)把當(dāng)前線(xiàn)程放入AQS阻塞隊(duì)列
        selfInterrupt();
}

ReentrantLock源碼分析

之前說(shuō)過(guò),AQS并沒(méi)有提供可用的tryAcquire方法,tryAcquire方法需要子類(lèi)自己定制化,所以這里代碼會(huì)調(diào)用ReentantLock重寫(xiě)的tryAcquire方法。我們看下非公平鎖????的實(shí)現(xiàn)

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {//當(dāng)前AQS狀態(tài)為0,acquires參數(shù)傳遞默認(rèn)為1,因?yàn)橹癈AS失敗,再次獲取鎖
        if (compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài)值為1
            setExclusiveOwnerThread(current);//設(shè)置該鎖的持有者為當(dāng)前的線(xiàn)程
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線(xiàn)程是該鎖的持有者
        int nextc = c + acquires;//獲取過(guò)了就累加,因?yàn)榭芍厝?
        if (nextc < 0) // overflow//說(shuō)明可重入次數(shù)溢出了
            throw new Error("Maximum lock count exceeded");
        setState(nextc);//重新設(shè)置鎖的狀態(tài)
        return true;
    }
    return false;//如果當(dāng)前線(xiàn)程不是該鎖的持有者,則返回false,然后會(huì)放入AQS阻塞隊(duì)列
}

結(jié)束完非公平鎖????的實(shí)現(xiàn)代碼,回過(guò)頭來(lái)看看非公平在這里是怎么體現(xiàn)的。首先非公平是說(shuō)先嘗試獲取鎖的線(xiàn)程并不一定比后嘗試獲取鎖的線(xiàn)程優(yōu)先獲取鎖????。

而是使用了搶奪策略。那么下面我們看看公平鎖????是怎么實(shí)現(xiàn)公平的。

protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//當(dāng)前AQS狀態(tài)為0
            if (!hasQueuedPredecessors() &&//公平性策略,判斷隊(duì)列還有沒(méi)有其它node,要保證公平
                compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài)
                setExclusiveOwnerThread(current);//設(shè)置獲取鎖的線(xiàn)程
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線(xiàn)程是該鎖的持有者
            int nextc = c + acquires;//重入次數(shù)+1
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);//重新設(shè)置鎖的狀態(tài)
            return true;
        }
        return false;
    }
}

如上代碼所示,公平的tryAcquire策略與非公平的類(lèi)似,不同之處在于,代碼在設(shè)置CAS操作之前添加了hasQueuedPredecessors()方法,該方法是實(shí)現(xiàn)公平性的核心代碼。代碼如下

public final boolean hasQueuedPredecessors() {
   
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

2.2void lockInterruptibly()方法

該方法與lock()方法類(lèi)似,不同在于對(duì)中斷進(jìn)行響應(yīng),如果當(dāng)前線(xiàn)程在調(diào)用該方法時(shí),其它線(xiàn)程調(diào)用了當(dāng)前線(xiàn)程的interrupt()方法,則該線(xiàn)程拋出異常而返回

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())//如果當(dāng)前線(xiàn)程被中斷,則直接拋出異常
        throw new InterruptedException();
    if (!tryAcquire(arg))//嘗試獲取資源
        doAcquireInterruptibly(arg);//調(diào)用AQS可被中斷的方法
}

2.3 boolean tryLock()方法

嘗試獲取鎖,如果當(dāng)前鎖沒(méi)用被其它線(xiàn)程持有,則當(dāng)前線(xiàn)程獲取該鎖并返回true,否則返回false。注意,該方法不會(huì)引起當(dāng)前線(xiàn)程阻塞

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;
}

如上代碼與非公平鎖的tryAcquire()方法代碼類(lèi)似,所以tryLock()使用的是非公平策略。

2.4 boolean tryLock(long timeout, TimeUnit unit)方法

嘗試獲取鎖,與tryLock()的不同之處在于,它設(shè)置了超時(shí)時(shí)間,如果超時(shí)時(shí)間到了,沒(méi)用獲取到鎖,則返回false,以下是相關(guān)代碼

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));//調(diào)用AQS的tryAcquireNanos方法
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}
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;
            }
            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);
    }
}

3 釋放鎖相關(guān)方法

3.1 void unlock()方法

嘗試獲取鎖,如果當(dāng)前線(xiàn)程持有鎖,則調(diào)用該方法會(huì)讓該線(xiàn)程持有的AQS狀態(tài)值減1,如果減1后當(dāng)前狀態(tài)值為0,則當(dāng)前線(xiàn)程會(huì)釋放該鎖,否則僅僅減1而已。

如果當(dāng)前線(xiàn)程沒(méi)用持有該鎖而調(diào)用了該方法則會(huì)拋出異常,代碼如下:

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

ReentrantLock源碼分析

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;//AQS狀態(tài)值減1
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {//如果當(dāng)前可重入次數(shù)為0,則清空鎖持有線(xiàn)程
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);//設(shè)置可重入次數(shù)為原始值減1
    return free;
}

4.案例介紹

下面使用ReentrantLock來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線(xiàn)程安全的list集合

public class ReentrantLockList {
    //線(xiàn)程不安全的list
    private ArrayList<String>arrayList=new ArrayList<>();
    //獨(dú)占鎖
    private volatile ReentrantLock lock=new ReentrantLock();

    //添加元素
    public void add(String e){
        lock.lock();
        try {
            arrayList.add(e);
        }finally {
            lock.unlock();
        }
    }
    //刪除元素
    public void remove(String e){
        lock.lock();
        try {
            arrayList.remove(e);
        }finally {
            lock.unlock();
        }
    }
    //獲取數(shù)據(jù)
    public String get(int index){
        lock.lock();
        try {
            return arrayList.get(index);
        }finally {
            lock.unlock();
        }
    }
}

如上代碼在操作arrayList元素前進(jìn)行加鎖保證同一時(shí)間只有一個(gè)線(xiàn)程可用對(duì)arrayList數(shù)組進(jìn)行修改,但是也只能一個(gè)線(xiàn)程對(duì)arrayList進(jìn)行訪(fǎng)問(wèn)。

ReentrantLock源碼分析

如圖,假如線(xiàn)程Thread-1,Thread-2,Thread-3同時(shí)嘗試獲取獨(dú)占鎖ReentrantLock,加上Thread-1獲取到了????,則Thread-2和Thread-3就會(huì)被轉(zhuǎn)換為Node節(jié)點(diǎn)并放入ReentrantLock對(duì)應(yīng)的AQS阻塞隊(duì)列,而后阻塞掛起。

ReentrantLock源碼分析

如圖,假設(shè)Thread-1獲取鎖后調(diào)用了對(duì)應(yīng)的鎖創(chuàng)建的條件變量1,那么Thread-1就會(huì)釋放獲取到的????,然后當(dāng)前線(xiàn)程就會(huì)被轉(zhuǎn)換為Node節(jié)點(diǎn)插入條件變量1的條件隊(duì)列。由于Thread-1釋放了????,所以阻塞到AQS隊(duì)列里面的

Thread-2和Thread-3就會(huì)有機(jī)會(huì)獲取到該鎖,假如使用的是公平性策略,那么者時(shí)候Thread-2會(huì)獲取到鎖,從而從AQS隊(duì)列里面移除Thread-2對(duì)應(yīng)的Node節(jié)點(diǎn)。

“ReentrantLock源碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI