JUC系列 - ReentrantLock源碼解析

ReentrantLock仰楚,可重入鎖,是一種遞歸無阻塞的同步機制敦姻。它可以等同于synchronized的使用宛徊,但是ReentrantLock提供了比synchronized更強大崔梗、靈活的鎖機制夜只,可以減少死鎖發(fā)生的概率。

A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock. This can be checked using methods isHeldByCurrentThread, and getHoldCount.

本文將結(jié)合 **JDK1.7 ** 源碼來分析java.util.concurrent.locks.ReentrantLock內(nèi)部實現(xiàn)原理蒜魄。

首先扔亥,看看ReentrantLock 的構(gòu)造方法:

    public class ReentrantLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = 7373984872572414699L;
        
        private final Sync sync;

        /**
         * 默認構(gòu)造方法
         */
        public ReentrantLock() {
            sync = new NonfairSync();
        }

        /**
         * 帶fair參數(shù)的構(gòu)造方法
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    }

Sync為ReentrantLock里面的一個內(nèi)部類,它繼承AQS(AbstractQueuedSynchronizer)谈为,它有兩個子類:公平鎖FairSync和非公平鎖NonfairSync旅挤。

獲取鎖

ReentrantLock的lock方法如下:

    public void lock() {
        sync.lock();
    }

下面我們看非公平鎖(NonfairSync)的lock()方法:

    final void lock() {
        if (compareAndSetState(0, 1))   //嘗試獲取鎖
            setExclusiveOwnerThread(Thread.currentThread());
        else    //獲取鎖失敗
            acquire(1);
    }

首先會第一次嘗試快速獲取鎖,如果獲取失敗伞鲫,則調(diào)用acquire(int arg)方法粘茄,該方法定義在AQS中,如下:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

這個方法首先調(diào)用tryAcquire(int arg)方法秕脓,在AQS中講述過柒瓣,tryAcquire(int arg)需要自定義同步組件提供實現(xiàn),非公平鎖實現(xiàn)如下:

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    
    final boolean nonfairTryAcquire(int acquires) {
        //當前線程
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {   //state == 0,表示沒有該鎖處于空閑狀態(tài)
            if (compareAndSetState(0, acquires)) {
                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;
    }

首先判斷同步狀態(tài)state 是否為0吠架,如果為0表示該鎖還沒有被線程持有芙贫,直接通過CAS獲取同步狀態(tài),CAS成功則返回true傍药。如果state不為0磺平,則判斷當前線程是否為獲取鎖的線程,如果是則獲取鎖拐辽,成功返回true拣挪。

釋放鎖

獲取同步鎖后,使用完畢則需要釋放鎖俱诸,ReentrantLock提供了unlock釋放鎖:

    public void unlock() {
        sync.release(1);
    }

unlock方法內(nèi)部調(diào)用了Sync的release(int arg)釋放鎖菠劝,release(int arg)是在AQS中定義的:

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

與獲取同步狀態(tài)的acquire(int arg)方法相似,釋放同步狀態(tài)的tryRelease(int arg)同樣是調(diào)用Sync的tryRelease方法:

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

公平鎖與非公平鎖

公平鎖與非公平鎖的區(qū)別在于獲取鎖的時候是否按照FIFO的順序來乙埃。釋放鎖不存在公平性和非公平性闸英,上面以非公平鎖為例,下面我們來看看公平鎖(FairSync)的tryAcquire(int arg):

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

比較非公平鎖和公平鎖獲取同步狀態(tài)的過程介袜,會發(fā)現(xiàn)兩者唯一的區(qū)別就在于公平鎖在獲取同步狀態(tài)時多了一個限制條件:hasQueuedPredecessors()甫何,定義如下:

public final boolean hasQueuedPredecessors() {
        
        Node t = tail; //尾節(jié)點
        Node h = head;  //頭節(jié)點
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

該方法主要做一件事情:主要是判斷當前線程是否位于CLH同步隊列中的第一個。如果是則返回true遇伞,否則返回false辙喂。

ReentrantLock與synchronized的區(qū)別

ReentrantLock在加鎖和內(nèi)存上提供的語義與內(nèi)置鎖相同,區(qū)別在于:

  • ReentrantLock提供了一些其他功能,具備更強的擴展性巍耗,包括:定時的鎖等待秋麸,可中斷的鎖等候,公平性炬太。
  • ReentrantLock提供了條件(Condition)灸蟆,對線程的等待、喚醒操作更加詳細和靈活亲族。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炒考,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子霎迫,更是在濱河造成了極大的恐慌斋枢,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件知给,死亡現(xiàn)場離奇詭異瓤帚,居然都是意外死亡,警方通過查閱死者的電腦和手機涩赢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門戈次,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谒主,你說我怎么就攤上這事朝扼。” “怎么了霎肯?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵擎颖,是天一觀的道長。 經(jīng)常有香客問我观游,道長搂捧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上建椰,老公的妹妹穿的比我還像新娘。我一直安慰自己聋丝,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布工碾。 她就那樣靜靜地躺著弱睦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渊额。 梳的紋絲不亂的頭發(fā)上况木,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天垒拢,我揣著相機與錄音,去河邊找鬼火惊。 笑死求类,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的屹耐。 我是一名探鬼主播尸疆,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼张症!你這毒婦竟也來了仓技?” 一聲冷哼從身側(cè)響起鸵贬,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤俗他,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阔逼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兆衅,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年嗜浮,在試婚紗的時候發(fā)現(xiàn)自己被綠了羡亩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡危融,死狀恐怖畏铆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吉殃,我是刑警寧澤辞居,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蛋勺,受9級特大地震影響瓦灶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抱完,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一贼陶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巧娱,春花似錦碉怔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至上荡,卻和暖如春趴樱,著一層夾襖步出監(jiān)牢的瞬間馒闷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工叁征, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纳账,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓捺疼,卻偏偏與公主長得像疏虫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子啤呼,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容