干貨,深入剖析ReentrantLock源碼,推薦收藏

ReentrantLock和Synchronized都是Java開發中最常用的鎖,與Synchronized這種JVM內置鎖不同的是,ReentrantLock提供了更豐富的語義 ??梢詣摻ü芥i或非公平鎖、響應中斷、超時等待、按條件喚醒等 。在某些場景下,使用ReentrantLock更適合,功能更強大 。
前兩篇文章,我們分析了AQS的加鎖流程、以及源碼實現 。當時我們就說了,AQS使用了模板設計模式,父類中定義加鎖流程,子類去實現具體的加鎖邏輯 。所以大部分加鎖代碼已經在父類AQS中實現了,導致ReentrantLock的源碼非常簡單,一塊學習一下 。
先看一下ReentrantLock怎么使用?
1. ReentrantLock的使用/** * @author 一燈架構 * @apiNote ReentrantLock示例 **/public class ReentrantLockDemo {public static void main(String[] args) {// 1. 創建ReentrantLock對象ReentrantLock lock = new ReentrantLock();// 2. 加鎖lock.lock();try {// 3. 這里執行具體的業務邏輯} finally {// 4. 釋放鎖lock.unlock();}}}可以看到ReentrantLock的使用非常簡單,調用lock加鎖,unlock釋放鎖,需要配置try/finally使用,保證在代碼執行出錯的時候也能釋放鎖 。
ReentrantLock也可以配合Condition條件使用,具體可以翻一下前幾篇文章中BlockingQueue的源碼解析,那里面有ReentrantLock的實際使用 。
再看一下ReentrantLock的類結構
2. ReentrantLock類結構// 實現Lock接口public class ReentrantLock implements Lock {// 只有一個Sync同步變量private final Sync sync;// Sync繼承自AQS,主要邏輯都在這里面abstract static class Sync extends AbstractQueuedSynchronizer {}// Sync的兩個子類,分別實現了公平鎖和非公平鎖static final class FairSync extends Sync {}static final class NonfairSync extends Sync {}}可以看出ReentrantLock的類結構非常簡單,實現了Lock接口 。
類里面有兩個靜態內部類,分別實現公平鎖和非公平鎖 。
看一下Lock接口中,定義了哪些方法?
public interface Lock {// 加鎖void lock();// 加可中斷的鎖void lockInterruptibly() throws InterruptedException;// 嘗試加鎖boolean tryLock();// 一段時間內,嘗試加鎖boolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 釋放鎖void unlock();// 新建條件狀態Condition newCondition();}就是一些使用鎖的常用方法 。
在上篇文章中瀏覽AQS源碼的時候,了解到AQS定義了一些有關具體加鎖、釋放鎖的抽象方法 , 留給子類去實現 , 再看一下有哪些抽象方法:
// 加獨占鎖protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}// 釋放獨占鎖protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();}// 加共享鎖protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();}// 釋放共享鎖protected boolean tryReleaseShared(int arg) {throw new UnsupportedOperationException();}// 判斷是否是當前線程正在持有鎖protected boolean isHeldExclusively() {throw new UnsupportedOperationException();}由于ReentrantLock使用的是獨占鎖 , 所以只需要實現獨占鎖相關的方法就可以了 。
3. ReentrantLock源碼解析3.1 ReentrantLock構造方法// 默認的構造方法,使用非公平鎖public ReentrantLock() {sync = new NonfairSync();}// 傳true,可以指定使用公平鎖public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}在創建ReentrantLock對象的時候,可以指定使用公平鎖還是非公平鎖,默認使用非公平鎖,顯然非公平鎖的性能更好 。
先思考一個面試??紗栴},公平鎖和非公平鎖是怎么實現的?
3.2 非公平鎖源碼先看一下加鎖源碼:
從父類ReentrantLock的加鎖方法入口:
public class ReentrantLock implements Lock {// 加鎖入口方法public void lock() {// 調用Sync中加鎖方法sync.lock();}}在子類NonfairSync的加鎖方法:
// 非公平鎖static final class NonfairSync extends Sync {// 加鎖final void lock() {// 1. 先嘗試加鎖(使用CAS設置state=1)if (compareAndSetState(0, 1))// 2. 加鎖成功,就把當前線程設置為持有鎖線程setExclusiveOwnerThread(Thread.currentThread());else// 3. 沒加鎖成功,再調用父類AQS中實際的加鎖邏輯acquire(1);}}加鎖邏輯也很簡單 , 先嘗試使用CAS加鎖(也就是把state從0設置成1),加鎖成功,就把當前線程設置為持有鎖線程 。
設計者很聰明,在鎖競爭不激烈的情況下,很大概率可以加鎖成功,也就不用走else中復雜的加鎖邏輯了 。
如果沒有加鎖成功,還是需要走else中調用父類AQS的acquire方法,而acquire又需要調用子類的tryAcquire方法 。
調用鏈路就是下面這樣:

推薦閱讀