亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

J.U.C|AQS共享式源碼分析

ghnor / 925人閱讀

摘要:主要講解方法共享式獲取同步狀態(tài),返回值表示獲取成功,反之則失敗。源碼分析同步器的和方法請(qǐng)求共享鎖的入口當(dāng)并且時(shí)才去才獲取資源獲取鎖以共享不可中斷模式獲取鎖將當(dāng)前線程一共享方式構(gòu)建成節(jié)點(diǎn)并將其加入到同步隊(duì)列的尾部。

一、寫在前面

上篇給大家聊了獨(dú)占式的源碼,具體參見《J.U.C|AQS獨(dú)占式源碼分析》

這一章我們繼續(xù)在AQS的源碼世界中遨游,解讀共享式同步狀態(tài)的獲取和釋放。

二、什么是共享式

共享式與獨(dú)占式唯一的區(qū)別是在于同一時(shí)刻可以有多個(gè)線程獲取到同步狀態(tài)。

我們以讀寫鎖為例來看兩者,一個(gè)線程在對(duì)一個(gè)資源文件進(jìn)行讀操作時(shí),那么這一時(shí)刻對(duì)于文件的寫操作均被阻塞,而其它線程的讀操作可以同時(shí)進(jìn)行。
當(dāng)寫操作要求對(duì)資源獨(dú)占操作,而讀操作可以是共享的,兩種不同的操作對(duì)同一資源進(jìn)行操作會(huì)是什么樣的?看下圖


共享式訪問資源,其他共享時(shí)均被允許,而獨(dú)占式被阻塞。


獨(dú)占式訪問資源時(shí),其它訪問均被阻塞。

通過讀寫鎖給大家一起溫故下獨(dú)占式和共享式概念,上一節(jié)我們已經(jīng)聊過獨(dú)占式,本章我們主要聊共享式。

主要講解方法

protected int tryAcquireShared(int arg);共享式獲取同步狀態(tài),返回值 >= 0 表示獲取成功,反之則失敗。

protected boolean tryReleaseShared(int arg): 共享式釋放同步狀態(tài)。

三、核心方法分析 3.1 同步狀態(tài)的獲取

public final void acquireShared(int arg)

共享式獲取同步狀態(tài)的頂級(jí)入口,如果當(dāng)前線程未獲取到同步狀態(tài),將會(huì)加入到同步隊(duì)列中等待,與獨(dú)占式唯一的區(qū)別是在于同一時(shí)刻可以有多個(gè)線程獲取到同步狀態(tài)。

方法源碼

public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

方法函數(shù)解析

tryAcquireShared(arg):獲取同步狀態(tài),返回值大于等于0表示獲取成功,否則失敗。

doAcquireShared(arg):共享式獲取共享狀態(tài),包含構(gòu)建節(jié)點(diǎn),加入隊(duì)列等待,喚醒節(jié)點(diǎn)等操作。

源碼分析

同步器的 acquireShared 和 doAcquireShared 方法

//請(qǐng)求共享鎖的入口
public final void acquireShared(int arg) {
        // 當(dāng)state != 0 并且tryAcquireShared(arg) < 0 時(shí)才去才獲取資源
        if (tryAcquireShared(arg) < 0)
            // 獲取鎖
            doAcquireShared(arg);
    }
// 以共享不可中斷模式獲取鎖
private void doAcquireShared(int arg) {
        // 將當(dāng)前線程一共享方式構(gòu)建成 node 節(jié)點(diǎn)并將其加入到同步隊(duì)列的尾部。這里addWaiter(Node.SHARED)操作和獨(dú)占式基本一樣,
        final Node node = addWaiter(Node.SHARED);
        // 是否成功標(biāo)記
        boolean failed = true;
        try {
            // 等待過程是否被中斷標(biāo)記
            boolean interrupted = false;
            自旋
            for (;;) {
                // 獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
                final Node p = node.predecessor();
                // 判斷前驅(qū)節(jié)點(diǎn)是否是head節(jié)點(diǎn),也就是看自己是不是老二節(jié)點(diǎn)
                if (p == head) {
                    // 如果自己是老二節(jié)點(diǎn),嘗試獲取資源鎖,返回三種狀態(tài)
                    // state < 0 : 表示獲取資源失敗
                    // state = 0: 表示當(dāng)前正好線程獲取到資源, 此時(shí)不需要進(jìn)行向后繼節(jié)點(diǎn)傳播。
                    // state > 0: 表示當(dāng)前線程獲取資源鎖后,還有多余的資源,需要向后繼節(jié)點(diǎn)繼續(xù)傳播,獲取資源。 
                    int r = tryAcquireShared(arg);
                    // 獲取資源成功
                    if (r >= 0) {
                        // 當(dāng)前節(jié)點(diǎn)線程獲取資源成功后,對(duì)后繼節(jié)點(diǎn)進(jìn)行邏輯操作
                        setHeadAndPropagate(node, r);
                        // setHeadAndPropagate(node, r) 已經(jīng)對(duì)node.prev = null,在這有對(duì)p.next = null; 等待GC進(jìn)行垃圾收集。
                        p.next = null; // help GC
                        // 如果等待過程被中斷了, 將中斷給補(bǔ)上。
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                // 判斷狀態(tài),尋找安全點(diǎn),進(jìn)入waiting狀態(tài),等著被unpark()或interrupt()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

在acquireShared(int arg)方法中,同步器調(diào)用tryAcquireShared(arg)方法獲取同步狀態(tài),返回同步狀態(tài)有兩種。

當(dāng)同步狀態(tài)大于等于0時(shí): 表示可以獲取到同步狀態(tài),退出自旋,在doAcquireShared(int arg)方法中可以看到節(jié)點(diǎn)獲取資源退出自旋的條件就是大于等于0

小于0會(huì)加入同步隊(duì)列中等待被喚醒。

addWaiter和enq方法

// 創(chuàng)建節(jié)點(diǎn),并將節(jié)點(diǎn)加入到同步隊(duì)列尾部中。
 private Node addWaiter(Node mode) {
        // 以共享方式為線程構(gòu)建Node節(jié)點(diǎn)
        Node node = new Node(Thread.currentThread(), mode);
        // 嘗試快速加入到隊(duì)列尾部
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            // CAS保證原子操作,將node節(jié)點(diǎn)加入到隊(duì)列尾部
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 快速加入失敗,走 enq(node)方法
        enq(node);
        return node;
}
//以自旋的方式,將node節(jié)點(diǎn)加入到隊(duì)列的尾部
private Node enq(final Node node) {
        // 自旋
        for (;;) {
            // 獲取尾部節(jié)點(diǎn)
            Node t = tail;
            // 如果tail節(jié)點(diǎn)為空, 說明同步隊(duì)列還沒初始化,必須先進(jìn)行初始化
            if (t == null) { // Must initialize
                // CAS保證原子操作, 新建一個(gè)空 node 節(jié)點(diǎn)并將其設(shè)置為head節(jié)點(diǎn)
                if (compareAndSetHead(new Node()))
                    // 設(shè)置成功并將tail也指向該節(jié)點(diǎn)
                    tail = head;
            } else {
                // 將node節(jié)點(diǎn)加入到隊(duì)列尾部
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

這兩個(gè)方法和獨(dú)占式的基本相同,注釋中都標(biāo)明了,在這就不多做解釋了。

獲取資源成功后對(duì)后繼節(jié)點(diǎn)的操作setHeadAndPropagate方法

private void setHeadAndPropagate(Node node, int propagate) {
        // 記錄老的head節(jié)點(diǎn),以便核對(duì)
        Node h = head; // Record old head for check below
        // 將node 設(shè)置成head節(jié)點(diǎn)
        setHead(node);
        // 這里表示: 如果資源足夠(propagate > 0)或者舊頭節(jié)點(diǎn)為空(h == null)或者舊節(jié)點(diǎn)的waitStatus為 SIGNAL(-1) 或者 PROPAGATE(-3)(h.waitStatus < 0)
        // 或者當(dāng)前head節(jié)點(diǎn)不為空或者waitStatus為SIGNAL(-1) 或者 PROPAGATE(-3),此時(shí)需要繼續(xù)喚醒后繼節(jié)點(diǎn)來嘗試獲取資源。
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            // 當(dāng)前node節(jié)點(diǎn)的后繼節(jié)點(diǎn)
            Node s = node.next;
            //如果后節(jié)點(diǎn)為空或者屬于共享節(jié)點(diǎn)
            if (s == null || s.isShared())
                // 繼續(xù)嘗試獲取資源
                doReleaseShared();
        }
    }

首先將當(dāng)前節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn) setHead(node), 其次根據(jù)條件看是否對(duì)后繼節(jié)點(diǎn)繼續(xù)喚醒。

獲取資源失敗進(jìn)行阻塞等待unpark

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 獲取前驅(qū)節(jié)點(diǎn)的等待狀態(tài)
        int ws = pred.waitStatus;
        // 如果等待狀態(tài)已經(jīng)為SIGNAL(表示當(dāng)前當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)處于等待狀態(tài),如果當(dāng)前節(jié)點(diǎn)釋放了同步狀態(tài)或者被中斷, 則會(huì)喚醒后繼節(jié)點(diǎn))
        if (ws == Node.SIGNAL)
            // 直接返回,表示可以安心的去休息了
            return true;
        // 如果前驅(qū)的節(jié)點(diǎn)的狀態(tài) ws > 0(表示該節(jié)點(diǎn)已經(jīng)被取消或者中斷,也就是成無效節(jié)點(diǎn),需要從同步隊(duì)列中取消的)
        if (ws > 0) {
            // 循環(huán)往前需尋找,知道尋找到一個(gè)有效的安全點(diǎn)(一個(gè)等待狀態(tài)<= 0 的節(jié)點(diǎn),排在它后面)
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            // 注意這一波操作后,獲獎(jiǎng)取消的節(jié)點(diǎn)全部變成GC可回收的廢棄鏈。
            pred.next = node;
        } else {
            //如果前驅(qū)正常,那就把前驅(qū)的狀態(tài)設(shè)置成SIGNAL,告訴它獲取資源后通知自己一下。有可能失敗,人家說不定剛剛釋放完呢!
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

 private final boolean parkAndCheckInterrupt() {
        // 調(diào)用park方法使當(dāng)前節(jié)點(diǎn)的線程進(jìn)入waiting
        LockSupport.park(this);    
        //返回線程中斷狀態(tài)
        return Thread.interrupted();
    }

這兩個(gè)方法和獨(dú)占式基本相同。

接著看doReleaseShared 這個(gè)比較復(fù)雜

private void doReleaseShared() {
        //注意,這里的頭結(jié)點(diǎn)已經(jīng)是上面新設(shè)定的頭結(jié)點(diǎn)了,從這里可以看出,如果propagate=0,
        //不會(huì)進(jìn)入doReleaseShared方法里面,那就有共享式變成了獨(dú)占式
        for (;;) { // 死循環(huán)以防在執(zhí)行此操作時(shí)添加新節(jié)點(diǎn):退出條件 h == head
            Node h = head;
            // 前提條件,當(dāng)前的頭節(jié)點(diǎn)不為空,并且不是尾節(jié)點(diǎn)
            if (h != null && h != tail) {
                // 當(dāng)前頭節(jié)點(diǎn)的等待狀態(tài)
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    // 如果當(dāng)前節(jié)點(diǎn)的狀態(tài)為SIGNAL,則利用CAS將其狀態(tài)設(shè)置為0(也就是初始狀態(tài))
                    //這里不直接設(shè)為Node.PROPAGATE,是因?yàn)閡nparkSuccessor(h)中,如果ws < 0會(huì)設(shè)置為0,所以ws先設(shè)置為0,再設(shè)置為PROPAGATE
                    //這里需要控制并發(fā),因?yàn)槿肟谟衧etHeadAndPropagate跟release兩個(gè),避免兩次unpark
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases 設(shè)置失敗,重新循環(huán)
                    // 喚醒后繼節(jié)點(diǎn)
                    unparkSuccessor(h);
                }
                // 如果等待狀態(tài)不為0 則利用CAS將其狀態(tài)設(shè)置為PROPAGATE ,以確保在釋放資源時(shí)能夠繼續(xù)通知后繼節(jié)點(diǎn)。
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed 如果head 期間發(fā)生了改變,則需要從新循壞
                break;
        }
    }
private void unparkSuccessor(Node node) {
        
        int ws = node.waitStatus;
        // 在此再次判斷當(dāng)前頭節(jié)點(diǎn)的的狀態(tài),如果小于0 將設(shè)置為0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        //獲取后繼節(jié)點(diǎn)
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            //如果后繼節(jié)點(diǎn)為空或者等待狀態(tài)大于0 直接放棄。
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                // 循環(huán)從尾部往前尋找下一個(gè)等待狀態(tài)不大于0的節(jié)點(diǎn)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // 喚醒該節(jié)點(diǎn)的線程
        if (s != null)
            LockSupport.unpark(s.thread);
    }

3.2 共享狀態(tài)釋放
最后一步釋放資源就比較簡(jiǎn)單了。

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
四、總結(jié)

在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)同步隊(duì)列,獲取狀態(tài)失敗的線程會(huì)加入到隊(duì)列中并進(jìn)行自旋,出列的(或者停止自旋)的條件時(shí)前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)并且成功獲取了同步狀態(tài)。在釋放同步狀態(tài)時(shí),調(diào)用Release方法釋放同步狀態(tài),然后喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。

共享式方式在喚醒后繼節(jié)點(diǎn)獲得資源后會(huì)判斷當(dāng)前資源是否還有多余的,如果有會(huì)繼續(xù)喚醒下一個(gè)節(jié)點(diǎn)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/74283.html

相關(guān)文章

  • J.U.C|AQS獨(dú)占源碼分析

    摘要:本章我們主要聊獨(dú)占式即同一時(shí)刻只能有一個(gè)線程獲取同步狀態(tài),其它獲取同步狀態(tài)失敗的線程則會(huì)加入到同步隊(duì)列中進(jìn)行等待。到這獨(dú)占式獲取同步和釋放同步狀態(tài)的源碼已經(jīng)分析完了。 一、寫在前面 上篇文章通過ReentrantLock 的加鎖和釋放鎖過程給大家聊了聊AQS架構(gòu)以及實(shí)現(xiàn)原理,具體參見《J.U.C|AQS的原理》。 理解了原理,我們?cè)趤砜纯丛賮硪徊揭徊降牧牧钠湓创a是如何實(shí)現(xiàn)的。 本章給...

    why_rookie 評(píng)論0 收藏0
  • J.U.C|可重入鎖ReentrantLock

    摘要:二什么是重入鎖可重入鎖,顧名思義,支持重新進(jìn)入的鎖,其表示該鎖能支持一個(gè)線程對(duì)資源的重復(fù)加鎖。將由最近成功獲得鎖,并且還沒有釋放該鎖的線程所擁有。可以使用和方法來檢查此情況是否發(fā)生。 一、寫在前面 前幾篇我們具體的聊了AQS原理以及底層源碼的實(shí)現(xiàn),具體參見 《J.U.C|一文搞懂AQS》《J.U.C|同步隊(duì)列(CLH)》《J.U.C|AQS獨(dú)占式源碼分析》《J.U.C|AQS共享式源...

    wangdai 評(píng)論0 收藏0
  • J.U.C|同步隊(duì)列(CLH)

    摘要:二什么是同步隊(duì)列同步隊(duì)列一個(gè)雙向隊(duì)列,隊(duì)列中每個(gè)節(jié)點(diǎn)等待前驅(qū)節(jié)點(diǎn)釋放共享狀態(tài)鎖被喚醒就可以了。三入列操作如上圖了解了同步隊(duì)列的結(jié)構(gòu),我們?cè)诜治銎淙肓胁僮髟诤?jiǎn)單不過。 一、寫在前面 在上篇我們聊到AQS的原理,具體參見《J.U.C|AQS原理》。 這篇我們來給大家聊聊AQS中核心同步隊(duì)列(CLH)。 二、什么是同步隊(duì)列(CLH) 同步隊(duì)列 一個(gè)FIFO雙向隊(duì)列,隊(duì)列中每個(gè)節(jié)點(diǎn)等待前驅(qū)...

    Nosee 評(píng)論0 收藏0
  • J.U.C|condition分析

    摘要:造成當(dāng)前線程在接到信號(hào)被中斷或到達(dá)指定最后期限之前一直處于等待狀態(tài)。該線程從等待方法返回前必須獲得與相關(guān)的鎖。如果線程已經(jīng)獲取了鎖,則將喚醒條件隊(duì)列的首節(jié)點(diǎn)。 一、寫在前面 在前幾篇我們聊了 AQS、CLH、ReentrantLock、ReentrantReadWriteLock等的原理以及其源碼解讀,具體參見專欄 《非學(xué)無以廣才》 這章我們一起聊聊顯示的Condition 對(duì)象。 ...

    Sourcelink 評(píng)論0 收藏0
  • J.U.C|讀-寫鎖ReentrantReadWriteLock

    摘要:所以就有了讀寫鎖。只要沒有,讀取鎖可以由多個(gè)線程同時(shí)保持。其讀寫鎖為兩個(gè)內(nèi)部類都實(shí)現(xiàn)了接口。讀寫鎖同樣依賴自定義同步器來實(shí)現(xiàn)同步狀態(tài)的,而讀寫狀態(tài)就是其自定義同步器的狀態(tài)。判斷申請(qǐng)寫鎖數(shù)量是否超標(biāo)超標(biāo)則直接異常,反之則設(shè)置共享狀態(tài)。 一、寫在前面 在上篇我們聊到了可重入鎖(排它鎖)ReentrantLcok ,具體參見《J.U.C|可重入鎖ReentrantLock》 Reentra...

    Tonny 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<