多執行緒 深入洞察 ReentrantLock

2021-09-20 09:38:05 字數 3366 閱讀 1387

工程原始碼來自:jdk 1.8

reentrantlock,可重入鎖,是一種遞迴無阻塞的同步機制。它可以等同於synchronized的使用,但是reentrantlock提供了比synchronized更強大、靈活的鎖機制,可以減少死鎖發生的概率。api介紹如下:

乙個可重入的互斥鎖定 lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖定相同的一些基本行為和語義,但功能更強大。reentrantlock 將由最近成功獲得鎖定,並且還沒有釋放該鎖定的執行緒所擁有。當鎖定沒有被另乙個執行緒所擁有時,呼叫 lock 的執行緒將成功獲取該鎖定並返回。如果當前執行緒已經擁有該鎖定,此方法將立即返回。可以使用 isheldbycurrentthread() 和 getholdcount() 方法來檢查此情況是否發生。

reentrantlock還提供了公平鎖也非公平鎖的選擇,構造方法接受乙個可選的公平引數(預設非公平鎖),當設定為true時,表示公平鎖,否則為非公平鎖。公平鎖與非公平鎖的區別在於公平鎖的鎖獲取是有順序的。但是公平鎖的效率往往沒有非公平鎖的效率高,在許多執行緒訪問的情況下,公平鎖表現出較低的吞吐量。

我們一般都是這麼使用reentrantlock獲取鎖的:

//非公平鎖

reentrantlock lock = new reentrantlock();

lock.lock();

lock方法:

public void lock()
sync為reentrantlock裡面的乙個內部類,它繼承aqs(abstractqueuedsynchronizer),它有兩個子類:公平鎖fairsync和非公平鎖nonfairsync。

reentrantlock裡面大部分的功能都是委託給sync來實現的,同時sync內部定義了lock()抽象方法由其子類去實現,預設實現了nonfairtryacquire(int acquires)方法,可以看出它是非公平鎖的預設實現方式。下面我們看非公平鎖的lock()方法:

final void lock()
首先會第一次嘗試快速獲取鎖,如果獲取失敗,則呼叫acquire(int arg)方法,該方法定義在aqs中,如下:

public final void acquire(int arg)
這個方法首先呼叫tryacquire(int arg)方法,在aqs中講述過,tryacquire(int arg)需要自定義同步元件提供實現,非公平鎖實現如下:

protected final boolean tryacquire(int acquires) 

final boolean nonfairtryacquire(int acquires)

} //執行緒重入

//判斷鎖持有的執行緒是否為當前執行緒

else if (current == getexclusiveownerthread())

return false;

}

該方法主要邏輯:首先判斷同步狀態state == 0 ?,如果是表示該鎖還沒有被執行緒持有,直接通過cas獲取同步狀態,如果成功返回true。如果state != 0,則判斷當前執行緒是否為獲取鎖的執行緒,如果是則獲取鎖,成功返回true。成功獲取鎖的執行緒再次獲取鎖,這是增加了同步狀態state。

獲取同步鎖後,使用完畢則需要釋放鎖,reentrantlock提供了unlock釋放鎖:

public void unlock()
unlock內部使用sync的release(int arg)釋放鎖,release(int arg)是在aqs中定義的:

public final boolean release(int arg) 

return false;

}

與獲取同步狀態的acquire(int arg)方法相似,釋放同步狀態的tryrelease(int arg)同樣是需要自定義同步元件自己實現:

protected final boolean tryrelease(int releases) 

setstate(c);

return free;

}

只有當同步狀態徹底釋放後該方法才會返回true。當state == 0 時,則將鎖持有執行緒設定為null,free= true,表示釋放成功。

公平鎖與非公平鎖的區別在於獲取鎖的時候是否按照fifo的順序來。釋放鎖不存在公平性和非公平性,上面以非公平鎖為例,下面我們來看看公平鎖的tryacquire(int arg):

protected final boolean tryacquire(int acquires) 

} else if (current == getexclusiveownerthread())

return false;

}

比較非公平鎖和公平鎖獲取同步狀態的過程,會發現兩者唯一的區別就在於公平鎖在獲取同步狀態時多了乙個限制條件:hasqueuedpredecessors(),定義如下:

public final boolean hasqueuedpredecessors()
該方法主要做一件事情:主要是判斷當前執行緒是否位於clh同步佇列中的第乙個。如果是則返回true,否則返回false。

reentrantlock與synchronized的區別

前面提到reentrantlock提供了比synchronized更加靈活和強大的鎖機制,那麼它的靈活和強大之處在**呢?他們之間又有什麼相異之處呢?

首先他們肯定具有相同的功能和記憶體語義。

reentrantlock還提供了條件condition,對執行緒的等待、喚醒操作更加詳細和靈活,所以在多個條件變數和高度競爭鎖的地方,reentrantlock更加適合(以後會闡述condition)。

reentrantlock提供了可輪詢的鎖請求。它會嘗試著去獲取鎖,如果成功則繼續,否則可以等到下次執行時處理,而synchronized則一旦進入鎖請求要麼成功要麼阻塞,所以相比synchronized而言,reentrantlock會不容易產生死鎖些。

reentrantlock支援更加靈活的同步**塊,但是使用synchronized時,只能在同乙個synchronized塊結構中獲取和釋放。注:reentrantlock的鎖釋放一定要在finally中處理,否則可能會產生嚴重的後果。

reentrantlock支援中斷處理,且效能較synchronized會好些。

上面的獲取鎖,釋放鎖過程中有很多方法都是組合使用了aqs中的方法,作為同步元件的基礎,aqs做了太多的工作,自定義同步元件只需要簡單地實現自定義方法,然後加上aqs提供的模板方法,就可以實現強大的自定義同步元件

C 深入多執行緒

主線程 th thread.currentthread 現在的執行緒為主執行緒 th.name mainthread set執行緒名字 主線程本身沒有名字 thread1.text th.name get執行緒名字 建立執行緒 建立執行緒 threadstart thread action new ...

五 多執行緒深入話題

1 優雅的停止執行緒 在多執行緒操作之中,啟動多執行緒使用的是thread類中的start 方法,而要對多執行緒進行停止處理,在原來的thread類中提供有stop 方法,但此方法在jdk1.2版本之後就已經過期了,不可使用。而除了stop 方法之外,以下幾個方法也被禁用了 方法方法定義 廢除原因 ...

多執行緒的深入理解

通過單執行緒程式的小例子來深入理解執行緒 程式 執行流程 通過程式執行結果可以看出,主線程建立兩個物件,然後呼叫d1.show 方法,主線程把show方法進棧,執行show方法中的 執行完show方法出棧,此時 d2.show 沒有被執行,緊接著呼叫d2.show 方法。程式執行簡圖 1 多執行緒例...