【認真學java—鎖】可重入鎖ReentrantLock

學習記錄,有不對的或更好看法,還請多多交流html

參考

需瞭解的知識

synchronized
不明白,能夠看如下文章,慢慢看,沉下心java

看完以上文章,就不用看本文了😀api

ReenentrantLock概述

ReentrantLock 是對synchronized的擴展。突出的特色就是可重入,更靈活。可重入是指什麼?那反過來問不可重入是什麼?
不可重入顧名思義就是不能夠重複進入,具體指的是在沒有釋放鎖的狀況下,不能夠再獲取鎖,不然就會形成死鎖dead lock。(不少名詞都是英文翻譯過來的,因此英文很重要,否則獲取的知識就是二手的)
錯誤例子以下:多線程

public void test(Object object){
    synchronized(object){           // first lock
        synchronized(object){       // second lock
            //do something
        }
    }
}

上面的second lock 永遠也獲取不到鎖,因此形成了死鎖,程序就卡死在第二次加鎖。oracle

主要原理:ReentrantLock類的內部有個Sync類繼承了AbstractQueuedSynchronizer(AQS封裝了多線程下隊列的同步操做)對鎖操做進行了封裝。NonfairSync和FairSync繼承了Sync類,並重載了各自的tryAcquire方法。經過不一樣的構造器ReentrantLock(),ReentrantLock(boolean fair)來實例化不一樣的Sync對象,以建立公平可重入鎖 / 不公平可重入鎖對象,後續的全部操做都是基於這個lock對象進行操做。less

而對於ReentrantLock來講,它提供了在鎖沒釋放時,能夠再次獲取鎖的方法 tryLock。先來看看ReentrantLock中重要的方法及含義:性能

  1. lock 和synchronized相似,僅當沒有其餘線程持有該對象的鎖時,才能加鎖成功。在其餘線程持有鎖期間,該線程會進去睡眠狀態,(dormant),直到獲取到鎖。區別在於:若本身對本身再次加鎖,是能夠得到鎖的。(可重入)(注:synchronized對本身加鎖兩次是死鎖)
  2. 構造器中的參數fairness,設爲true時,鎖會優先讓等待最長時間的線程持有;若爲false(默認爲false),則不保證線程持有鎖的前後順序。
    注意:fairness爲true,會嚴重影響總體性能。並且fairness並不保證線程的調度的公平(不保證每一個線程平均獲取鎖,只是讓排隊最長時間先的獲取而已)
  3. tryLock方法不受fairness設置影響。即便有其餘線程獲取了鎖,鎖仍是能夠被繼續獲取的,最大支持遞歸 2147483647次(int的最大整數)。(可重入

注tryLock()在沒得到鎖時,不會像lock()同樣進入睡眠等待;tryLock(long timeout, TimeUnit unit)會睡眠等待timeout時長,同時會fairness規則學習

  1. unlock釋放本身得到的鎖,使鎖的次數-1。

源碼:
一、Sync內部類nonfairTryAcquire方法ui

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {      //初始態,沒有線程持有鎖時
        if (compareAndSetState(0, acquires)) {  //見下文2
            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;
}

二、AQS中的compareAndSetState(int expect, int update)方法this

protected final boolean compareAndSetState(int expect, int update) {
    return STATE.compareAndSet(this, expect, update);   //見下文3
}

三、本地方法(C++編寫的)compareAndSet位於java.lang.invoke.VarHandle下(常說的CAS)
官網api連接:CAS

若是witness value == exceptedValue,則賦新值newVaule。其中witness value 爲main memory中該變量的值(getVolatile獲取);賦新值經過setVolatile賦值。
return true,賦值成功。
return false,變量被其餘線程改變了,賦值失敗。

四、NonfairSync類的tryAcquire就是調用Sync中nonfairTryAcquire(acquires);
五、FairSync中的tryAcquire

/**
 * Fair version of tryAcquire.  Don't grant access unless 
 * recursive call or no waiters or is first. 
 * 翻譯:公平鎖,只有在重複獲取鎖 / 沒有其餘等得更久的線程在等待獲取鎖 / 第一個獲取鎖時,
 * 才能獲取到鎖。
 */
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
     int c = getState();
     if (c == 0) {
            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;
}

六、hasQueuedPredecessors(AQS中)

/*
* Queries whether any threads have been waiting to acquire longer than the current thread.
* 翻譯:查看是否有其餘想獲取鎖的線程比當前線程等的長(公平鎖的原則:先服務等得久的)
*/
源碼略,由於須要先看懂AQS這個類的實現機制
相關文章
相關標籤/搜索