ReentrantLock源碼分析

以我第一次讀源碼的順序瘩蚪。

創(chuàng)建鎖:

private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;

public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

//主要邏輯都是基于這個大名鼎鼎的AQS
abstract static class Sync extends AbstractQueuedSynchronizer {}

加鎖

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

非公平鎖:

final void lock() {
    // 直接通過cas獲取鎖宴霸,state == 0鎖可用
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    //如果沒有拿到則
    else
        acquire(1);
}

protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

protected final void setExclusiveOwnerThread(Thread thread) {
      exclusiveOwnerThread = thread;
}

public final void acquire(int arg) {
     // addWaiter就是把當(dāng)前線程封裝成node妓柜,添加到等待隊列的尾端
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        //這里沒太想明白雀摘,什么狀態(tài)才會需要自我中斷
        selfInterrupt();
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // state == 0,沒有人占用鎖
    if (c == 0) {
        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;
}

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 如果符合park的條件苦掘,其實就是前序node狀態(tài)為-1换帜,則進入parkAndCheckInterrupt()函數(shù),其中調(diào)用LockSupport.park()實現(xiàn)線程的阻塞鹤啡;否則就嘗試賦值前序狀態(tài)至-1惯驼,然后再次嘗試獲取鎖,如果沒拿到則進入阻塞揉忘。
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        // ws > 0 -> ws = CANCEL跳座,把cancel節(jié)點從隊列中移除
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 如果前序的狀態(tài)為0或-2,-3泣矛,則嘗試將前序變?yōu)?1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

//這里沒太想明白疲眷,當(dāng)前線程什么時候會設(shè)置interrupt呢?
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

獲取鎖的邏輯都是在AQS里您朽,這里就需要看一下AQS的結(jié)構(gòu):

維護了一個雙向鏈表狂丝,用于存放等待鎖的線程node,包括head哗总,tail几颜。
sync的狀態(tài)status,0表示空閑讯屈,>0有占用蛋哭。
還有這幾個offset變量,存儲的是通過unsafe.objectFieldOffset方法獲取到的各變量的內(nèi)存偏移地址涮母。
AQS繼承AOS谆趾,AOS有個重要的變量exclusiveOwnerThread,存儲的就是當(dāng)前持有鎖的線程叛本。

Node包含:
prev沪蓬,next為前序后序節(jié)點,thread為所屬線程来候。
waitStatus包括如下幾種狀態(tài):

int CANCELLED = 1 //節(jié)點從同步隊列中取消
int SIGNAL = -1 //后繼線程處于等待狀態(tài)跷叉,如果當(dāng)前節(jié)點釋放同步狀態(tài)會通知后繼線程,使其能夠運行
int CONDITION = -2//當(dāng)前節(jié)點進入等待隊列中
int PROPAGATE = -3//表示下一次共享式同步狀態(tài)獲取將會無條件傳播下去
int INITIAL = 0;//初始狀態(tài)

公平鎖:

final void lock() {
    acquire(1);
}

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

public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

公平鎖相對于非公平鎖营搅,就多了一個邏輯hasQueuedPredecessors()云挟。
對于非公平鎖,當(dāng)某個線程嘗試獲取鎖的時候转质,如果正好碰到前一個線程釋放了鎖园欣,通過cas拿到鎖,那就可以了峭拘。而對于公平鎖俊庇,是按照AQS隊列的先后順序來獲取鎖的狮暑。

釋放鎖

公平和非公平鎖的釋放邏輯是一致的

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

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

// 如果釋放成功后state==0,則return true
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;
}

private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    //倒敘尋找離head最近的可喚醒線程
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}
?著作權(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é)果婚禮上巍虫,老公的妹妹穿的比我還像新娘。我一直安慰自己毁葱,他們只是感情好垫言,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布贰剥。 她就那樣靜靜地躺著倾剿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚌成。 梳的紋絲不亂的頭發(fā)上前痘,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音担忧,去河邊找鬼芹缔。 笑死,一個胖子當(dāng)著我的面吹牛瓶盛,可吹牛的內(nèi)容都是我干的最欠。 我是一名探鬼主播示罗,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芝硬!你這毒婦竟也來了蚜点?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤拌阴,失蹤者是張志新(化名)和其女友劉穎绍绘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迟赃,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡陪拘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纤壁。 大學(xué)時的朋友給我發(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
  • 正文 我出身青樓辟汰,卻偏偏與公主長得像列敲,于是被迫代替她去往敵國和親阱佛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361