亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談JAVA并發(fā)之ReentrantLock

 更新時間:2021年06月04日 10:44:57   作者:ukyu  
本文主要介紹了基于AQS實現(xiàn)的ReentrantLock(重入鎖),感興趣的同學(xué),可以參考下。

1. 介紹

結(jié)合上面的ReentrantLock類圖,ReentrantLock實現(xiàn)了Lock接口,它的內(nèi)部類Sync繼承自AQS,絕大部分使用AQS的子類需要自定義的方法存在Sync中。而ReentrantLock有公平與非公平的區(qū)別,即'是否先阻塞就先獲取資源',它的主要實現(xiàn)就是FairSync與NonfairSync,后面會從源碼角度看看它們的區(qū)別。

2. 源碼剖析

Sync是ReentrantLock控制同步的基礎(chǔ)。它的子類分為了公平與非公平。使用AQS的state代表獲取鎖的數(shù)量

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
        * Performs {@link Lock#lock}. The main reason for subclassing
        * is to allow fast path for nonfair version.
        */
    abstract void lock();

    ...
}

我們可以看出內(nèi)部類Sync是一個抽象類,繼承它的子類(FairSync與NonfairSync)需要實現(xiàn)抽象方法lock。

下面我們先從非公平鎖的角度來看看獲取資源與釋放資源的原理

故事就從就兩個變量開始:

// 獲取一個非公平的獨占鎖
/**
* public ReentrantLock() {
*    sync = new ReentrantLock.NonfairSync();
* }
*/
private Lock lock = new ReentrantLock();
// 獲取條件變量
private Condition condition = lock.newCondition();

2.1 上鎖(獲取資源)

lock.lock()
public void lock() {
    sync.lock();
}
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    // 獲取資源
    final void lock() {
        // 若此時沒有線程獲取到資源,直接設(shè)置當(dāng)前線程獨占訪問資源。
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // AQS的方法
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        // 實現(xiàn)在父類Sync中
        return nonfairTryAcquire(acquires);
    }
}

AQS的acquire

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// Sync實現(xiàn)的非公平的tryAcquire
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 此時若沒有線程獲取到資源,當(dāng)前線程就直接占用該資源
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 若當(dāng)前線程已經(jīng)占用了該資源,可以再次獲取該資源  ->這個行為就是可重入鎖的支撐
    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;
}

嘗試獲取資源的過程是非常簡單的,這里再貼一下acquire的流程

2.2 釋放資源

lock.unlock();
public void unlock() {
    // AQS的方法
    sync.release(1);
}

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

release的流程已經(jīng)剖析過了,接下來看看tryRelease的實現(xiàn)

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 這里可以看出若沒有持有鎖,就釋放資源,就會報錯
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

tryRelease的實現(xiàn)也很簡單,這里再貼一下release的流程圖

2.3 公平鎖與非公平鎖的區(qū)別

公平鎖與非公平鎖,即'是否先阻塞就先獲取資源', ReentrantLock中公平與否的控制就在tryAcquire中。下面我們看看,公平鎖的tryAcquire

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // (2.3.1)
                // sync queue中是否存在前驅(qū)結(jié)點
                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;
        }
    }

區(qū)別在代碼(2.3.1)

hasQueuedPredecessors

判斷當(dāng)前線程的前面有無其他線程排隊;若當(dāng)前線程在隊列頭部或者隊列為空返回false

public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    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());
}

結(jié)合下面的入隊代碼(enq), 我們分析hasQueuedPredecessors為true的情況:

1.h != t ,表示此時queue不為空; (s = h.next) == null, 表示另一個結(jié)點已經(jīng)運行了下面的步驟(2),還沒來得及運行步驟(3)。簡言之,就是B線程想要獲取鎖的同時,A線程獲取鎖失敗剛好在入隊(B入隊的同時,之前占有的資源的線程,剛好釋放資源)

2.h != t 且 (s = h.next) != null,表示此時至少有一個結(jié)點在sync queue中;s.thread != Thread.currentThread(),這個情況比較復(fù)雜,設(shè)想一下有這三個結(jié)點 A -> B C, A此時獲取到資源,而B此時因為獲取資源失敗正在sync queue阻塞,C還沒有獲取資源(還沒有執(zhí)行tryAcquire)。

時刻一:A釋放資源成功后(執(zhí)行tryRelease成功),B此時還沒有成功獲取資源(C執(zhí)行s = h.next時,B還在sync queue中且是老二)

時刻二: C此時執(zhí)行hasQueuedPredecessors,s.thread != Thread.currentThread()成立,此時s.thread表示的是B

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node())) // (1) 第一次初始化
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) { // (2) 設(shè)置queue的tail
                t.next = node; // (3)
                return t;
            }
        }
    }
}

Note that 1. because cancellations due to interrupts and timeouts may occur at any time, a true return does not guarantee that some other thread will acquire before the current thread(虛假true). 2. Likewise, it is possible for another thread to win a race to enqueue after this method has returned false, due to the queue being empty(虛假false).

這位大佬對hasQueuedPredecessors進行詳細的分析,他文中解釋了虛假true以及虛假false。我這里簡單解釋一下:

1.虛假true, 當(dāng)兩個線程都執(zhí)行tryAcquire,都執(zhí)行到hasQueuedPredecessors,都返回true,但是只有一個線程執(zhí)行compareAndSetState(0, acquires)成功

2.虛假false,當(dāng)一個線程A執(zhí)行doAcquireInterruptibly,發(fā)生了中斷,還沒有清除掉該結(jié)點時;此時,線程B執(zhí)行hasQueuedPredecessors時,返回true

以上就是淺談JAVA并發(fā)之ReentrantLock的詳細內(nèi)容,更多關(guān)于JAVA并發(fā)之ReentrantLock的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java用BigDecimal解決double類型相減時可能存在的誤差

    Java用BigDecimal解決double類型相減時可能存在的誤差

    這篇文章主要介紹了Java用BigDecimal解決double類型相減時可能存在的誤差,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Java SpringBoot的相關(guān)知識點詳解

    Java SpringBoot的相關(guān)知識點詳解

    這篇文章主要介紹了SpringBoot的相關(guān)知識點,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-10-10
  • 解析Java中如何獲取Spring中配置的bean

    解析Java中如何獲取Spring中配置的bean

    本篇文章是對在Java中如何獲取Spring中配置的bean進行了詳細的分析介紹,需要的朋友參考下
    2013-07-07
  • 使用Java反射獲取方法的全路徑名稱的步驟

    使用Java反射獲取方法的全路徑名稱的步驟

    Java反射是一個強大的特性,它允許程序在運行時查詢和修改類的行為,反射可以用于實現(xiàn)很多高級功能,比如動態(tài)代理、依賴注入等,本文將介紹如何使用Java反射來實現(xiàn)這一功能,需要的朋友可以參考下
    2024-07-07
  • 詳談java編碼互轉(zhuǎn)(application/x-www-form-urlencoded)

    詳談java編碼互轉(zhuǎn)(application/x-www-form-urlencoded)

    下面小編就為大家?guī)硪黄斦刯ava編碼互轉(zhuǎn)(application/x-www-form-urlencoded)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • SpringMVC中RequestBody注解的List參數(shù)傳遞方式

    SpringMVC中RequestBody注解的List參數(shù)傳遞方式

    這篇文章主要介紹了SpringMVC中RequestBody注解的List參數(shù)傳遞方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • springAOP的三種實現(xiàn)方式示例代碼

    springAOP的三種實現(xiàn)方式示例代碼

    這篇文章主要介紹了springAOP的三種實現(xiàn)方式,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot深入講解單元測試與熱部署應(yīng)用

    SpringBoot深入講解單元測試與熱部署應(yīng)用

    這篇文章介紹了SpringBoot單元測試與熱部署,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • Java 深入探究講解抽象工廠模式

    Java 深入探究講解抽象工廠模式

    當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個簡單的對象,而是多個位于不同產(chǎn)品等級結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)
    2022-04-04
  • Springboot整合Mybatis和SQLite的詳細過程

    Springboot整合Mybatis和SQLite的詳細過程

    這篇文章主要介紹了Springboot整合Mybatis和SQLite的詳細過程,本文通過圖文示例相結(jié)合給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-07-07

最新評論