reentrantlock是面試中的高頻考點,其中實現原理還是很有必要了解的。它與synchronized類似,都是互斥鎖,但具有更好的擴充套件性。reentrantlock是基於aqs實現的,遺忘的同學可以回顧一下aqs原始碼詳細解讀。
1.1 繼承關係概述
首先看一下繼承關係圖,對它整體的構造有乙個初步的認識。
reentrantlock內部類繼承樹
我們發現,reentrantlock實現了公平鎖和非公平鎖。都通過他們的父類sync來排程。
1.2 reentrantlock中的重要方法
構造方法:無參構造方法,預設建立非公平鎖;有參構造方法,並且fair==true時,建立公平鎖。
//維護了乙個sync,對於鎖的操作都交給sync來處理
private final sync sync;
public reentrantlock()
public reentrantlock(boolean fair)
獲取資源資源(鎖)的方法:可以看出,請求都是交給sync來排程的。
//請求鎖資源,會阻塞且不處理中斷請求,
//沒有呼叫unlock(),則會一直被阻塞。
public void lock()
//執行緒在請求lock並被阻塞時,如果被interrupt,則此執行緒會被喚醒並被要求處理
public void lockinterruptibly() throws interruptedexception
//嘗試獲取鎖,預設獲取的是非公平鎖,失敗後不會阻塞
//直接返回true或false
public boolean trylock()
//過載方法,在規定時間內獲取鎖,獲取不到則返回false
public boolean trylock(long timeout, timeunit unit)
throws interruptedexception
釋放資源(鎖)的方法:不管是公平還是非公平鎖,都會呼叫aqs.release(1),給當前執行緒持有鎖的數量-1。
public void unlock()
我們主要看非公平鎖與公平鎖獲取資源的方法,因為釋放資源的邏輯是一樣的。
2.1 sync獲取資源
sync中定義了獲取資源的總入口。具體的呼叫還是看實現類是什麼。
abstract void lock();
2.2 非公平鎖獲取資源
final void lock()
tryacquire():走的是sync.nofairtryacquire()。
protected final boolean tryacquire(int acquires)
nonfairtryacquire(int acquires):如果鎖空閒,則用cas修改state;如果鎖被占用,則判斷佔有者是不是自己,實現可重入。最終沒有獲取鎖到就返回false。
final boolean nonfairtryacquire(int acquires)
}//獲取失敗則先判斷當前執行緒是否是鎖的持有者
//這裡很重要,也是reentrantlock實現可重入的原因
else if (current == getexclusiveownerthread())
return false;
}
2.3 公平鎖獲取資源lock():也是阻塞的。與非公平鎖的區別是,不能直接通過cas修改state,而是直接走aqs.acquire()。
final void lock()
tryaquire():與非公平鎖類似,aqs.acquire()會呼叫這個鉤子方法。只不過多判斷了hasqueuedpredecessors(),判斷當前節點在等待佇列中是否有前驅節點,如果有,則說明有執行緒比當前執行緒更早的請求資源,根據公平性,當前執行緒請求資源失敗;如果當前節點沒有前驅節點,才有做後面的邏輯判斷的必要性。
protected final boolean tryacquire(int acquires)
}//實現可重入
else if (current == getexclusiveownerthread())
return false;
}
reentrantlock有3中獲取鎖的方法,lock(),trylock(),lockinterruptibly()。
3.1 trylock()--嘗試獲取資源
trylock():走的還是sync的方法,在指定時間內獲取鎖,直接返回結果。
public boolean trylock(long timeout, timeunit unit)
throws interruptedexception
tryacquirenanos():如果呼叫trylock的規定時間內嘗試方法,就會呼叫該方法,先判斷是否中斷,然後嘗試獲取資源,否則進入aqs.doacquirenanos()(這個方法在上篇文章有解釋)。在規定時間內自旋拿資源,拿不到則掛起再判斷是否被中斷。
public final boolean tryacquirenanos(int arg, long nanostimeout)
throws interruptedexception
3.2 lockinterruptibly()--獲取鎖時響應中斷lockinterruptibly():交給了排程者sync執行。
public void lockinterruptibly() throws interruptedexception
acquireinterruptibly():當嘗試獲取鎖失敗後,就進行阻塞可中斷的獲取鎖的過程。呼叫aqs.doacquireinterruptibly()(這個方法在上篇文章也有詳細解釋)。
public final void acquireinterruptibly(int arg)
throws interruptedexception
公平鎖與非公平鎖的釋放都是一樣的。通過前面的閱讀,可以知道,reentrantlock.release()呼叫的是sync.release(1)。本質還是進入aqs.release(1),下面看看其中的tryrelease()這個鉤子方法如何實現。
sync釋放資源
tryrelease():嘗試釋放鎖,徹底釋放後返回true。
protected final boolean tryrelease(int releases)
//設定釋放後的state
setstate(c);
return free;
}
1)reentrantlock是如何實現可重入的?
不管是公平鎖還是非公平鎖,在獲取鎖時呼叫的tryacquire()方法,獲取成功後會setexclusiveownerthread(current)。將本執行緒設定為主人,之後每次呼叫tryacquire()時,發現當前執行緒就是主人,直接返回true。
2)簡述公平鎖與非公平鎖的區別?
獲取鎖的順序與請求鎖的時間順序一致就是公平鎖,反之則為非公平鎖。公平鎖每次都是從同步佇列中的第乙個節點獲取到鎖,而非公平性鎖則不一定,有可能剛釋放鎖的執行緒能再次獲取到鎖。
公平鎖為了保證時間上的絕對順序,需要頻繁的上下文切換,而非公平鎖會降低一定的上下文切換,降低效能開銷。因此,reentrantlock預設選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統更大的吞吐量。
3)aqs中有哪些資源訪問模式?區別?
獨佔模式和共享模式。
只有乙個執行緒才能持有這個鎖就是獨佔模式,由node節點中的nextwait來標識。
reentrantlock就是乙個獨佔鎖;而writeandreadlock的讀鎖則能由多個執行緒同時獲取,但它的寫鎖則只能由乙個執行緒持有,因此它使用了兩種模式。
4)為什麼reentrantlock.lock()方法不能被其他執行緒中斷?
因為當前執行緒前面可能還有等待執行緒,在aqs.acquirequeued()的迴圈裡,執行緒會再次被阻塞。parkandcheckinterrupt()返回的是thread.interrupted(),不僅返回中斷狀態,還會清除中斷狀態,保證阻塞執行緒忽略中斷。
其實看完aqs原始碼後,reentrantlock就是個弟弟。在實現上其實並不複雜,實現了aqs的獨佔模式。希望看完之後能對可重入鎖,響應中斷鎖有更深入的理解。
AbstractCollection原始碼分析
abstractcollection抽象類提供了collection的骨架實現,collection分析請看 這裡直接看它的 是如何實現的.public abstract iterator iterator 該方法沒有實現.public abstract int size 該方法沒有實現.publi...
ThreadPoolExecutor原始碼閱讀
執行緒池解決兩個問題 一是復用執行緒,減少建立銷毀執行緒帶來系統開銷 二是限定系統資源使用邊界,避免大量執行緒消耗盡系統記憶體 適用於互不依賴,執行時間短,不需要對執行緒控制操作的執行緒 新增任務時,1.若執行緒數量小於corepoolsize,則新增執行緒執行任務 2.若執行緒數量大於等於core...
OrangePi One Android 原始碼編譯
一 系統環境搭建參照 二 lichee原始碼編譯 1.檢視help build.sh h2.配置核心 cd linux 3.4 make arch arm menuconfig 進入配置頁面,上下移動列表,空格是選擇列表,左右移動選擇退出選項 3.首次編譯執行清除 在 lichee linux3.4...