ReentrantLock-jdk1.8

ReentrantLock分為公平鎖和非公平鎖碍遍,默認的為非公平鎖

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

可以手動指定

   public ReentrantLock(boolean fair) {
       sync = fair ? new FairSync() : new NonfairSync();
   }
先從非公平鎖講起

1 lock

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

首先嘗試cas把state設(shè)置為1,如果成功則獲取獨占鎖衙解,失敗執(zhí)行acquire
2 acquire

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

2.1 tryAcquire

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

先獲取當前獨占鎖的數(shù)量:
1.如果為0,則cas獲取鎖,成功則獲取獨占鎖夷恍。
2.如果不為0,則檢查當前線程是否是重入鎖媳维,是就獨占鎖數(shù)量加一并返回酿雪。

2.2.1 addWaiter
如果tryAcquire都失敗,那么就執(zhí)行入隊操作侄刽。

    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

如果尾節(jié)點不為空执虹,執(zhí)行一次快速入隊操作,如果cas成功唠梨,就入隊成功袋励。
尾節(jié)點為空或者快速入隊不成功,for循環(huán)執(zhí)行入隊直到成功

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

這里有個生成空節(jié)點的操作当叭,因為在存在隊列的情況下茬故,隊列頭節(jié)點表示的是當前擁有鎖的線程

2.2.1 acquireQueued
節(jié)點入隊成功之后再執(zhí)行線程掛起

    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;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

注意 p == head && tryAcquire(arg)這個條件是這個函數(shù)的唯一出口,先看線程掛起部分p == head && tryAcquire(arg)

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

判斷前置節(jié)點的狀態(tài)蚁鳖,在這里分為三種情況:
1.Node.SIGNAL,可以喚醒后繼節(jié)點磺芭,表示可以掛起單錢節(jié)點線程,所以直接返回
2.大于0醉箕,表示取消狀態(tài)钾腺,則往前遍歷刪除節(jié)點直至非取消狀態(tài)
3.如果不是前兩者徙垫,將前置節(jié)點置為Node.SIGNAL

線程掛起

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

2.3 selfInterrupt

    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

這是一個比較細節(jié)的地方,結(jié)合掛起部分看放棒。
線程掛起的方式是采用LockSupport.park姻报,能夠響應(yīng)中斷,但是不會拋異常间螟。也就是說掛起的線程有可能被ReentrantLock之外的線程喚醒吴旋,這時候就需要重置中斷狀態(tài),并且保證線程正確被喚醒并獲取鎖的時候厢破,保持中斷狀態(tài)荣瑟。

3 unlock
采用LockSupport.unpark喚醒隊列下一個線程。

再來看公平鎖

其實差別就兩點:
1.沒有直接cas獲取鎖的操作
2.在獲取鎖數(shù)量為0的時候摩泪,不再直接進行cas操作笆焰,要先判斷下隊列中是否還有節(jié)點未執(zhí)行

        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;
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市见坑,隨后出現(xiàn)的幾起案子仙辟,更是在濱河造成了極大的恐慌,老刑警劉巖鳄梅,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠国,死亡現(xiàn)場離奇詭異,居然都是意外死亡戴尸,警方通過查閱死者的電腦和手機粟焊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孙蒙,“玉大人项棠,你說我怎么就攤上這事】媛停” “怎么了香追?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坦胶。 經(jīng)常有香客問我透典,道長,這世上最難降的妖魔是什么顿苇? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任峭咒,我火速辦了婚禮,結(jié)果婚禮上纪岁,老公的妹妹穿的比我還像新娘凑队。我一直安慰自己,他們只是感情好幔翰,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布漩氨。 她就那樣靜靜地躺著西壮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叫惊。 梳的紋絲不亂的頭發(fā)上款青,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音赋访,去河邊找鬼。 笑死缓待,一個胖子當著我的面吹牛蚓耽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旋炒,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼步悠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘫镇?” 一聲冷哼從身側(cè)響起鼎兽,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铣除,沒想到半個月后谚咬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡尚粘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年择卦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郎嫁。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秉继,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泽铛,到底是詐尸還是另有隱情尚辑,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布盔腔,位于F島的核電站杠茬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏弛随。R本人自食惡果不足惜澈蝙,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撵幽。 院中可真熱鬧灯荧,春花似錦、人聲如沸盐杂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厉斟,卻和暖如春挚躯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擦秽。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工码荔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人感挥。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓缩搅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親触幼。 傳聞我的和親對象是個殘疾皇子硼瓣,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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

  • ReentrantLock 介紹 一個可重入的互斥鎖,它具有與使用{synchronized}方法和語句訪問的隱式...
    tomas家的小撥浪鼓閱讀 4,049評論 1 4
  • 前言 上一篇文章《基于CAS操作的Java非阻塞同步機制》 分析了非同步阻塞機制的實現(xiàn)原理置谦,本篇將分析一種以非同步...
    Mars_M閱讀 4,800評論 5 9
  • 一堂鲤、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是媒峡,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,676評論 0 44
  • 陌上花——蘇軾 (一) 陌上花開蝴蝶飛瘟栖,江山猶似昔人非。 遺民幾度垂垂老谅阿,游女長歌緩緩歸慢宗。 (二) 陌上山花無數(shù)開...
    9128ed8f8e24閱讀 451評論 0 0
  • 之前在新聞廣播中聽到了這樣一個詞,叫做道德綁架奔穿。不知道大家了不了解道德綁架究竟是什么意思呢镜沽? 這個詞的興起不得不說...
    猴爸陪你讀書閱讀 4,749評論 0 2