成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入淺出AQS之共享鎖模式

Berwin / 2377人閱讀

摘要:其二如果返回值等于表示當(dāng)前線程獲取共享鎖成功,但它后續(xù)的線程是無(wú)法繼續(xù)獲取的,也就是不需要把它后面等待的節(jié)點(diǎn)喚醒。

在了解了AQS獨(dú)占鎖模式以后,接下來(lái)再來(lái)看看共享鎖的實(shí)現(xiàn)原理。

原文地址:http://www.jianshu.com/p/1161...

搞清楚AQS獨(dú)占鎖的實(shí)現(xiàn)原理之后,再看共享鎖的實(shí)現(xiàn)原理就會(huì)輕松很多。兩種鎖模式之間很多通用的地方本文只會(huì)簡(jiǎn)單說(shuō)明一下,就不在贅述了,具體細(xì)節(jié)可以參考我的上篇文章深入淺出AQS之獨(dú)占鎖模式

一、執(zhí)行過(guò)程概述

獲取鎖的過(guò)程:

當(dāng)線程調(diào)用acquireShared()申請(qǐng)獲取鎖資源時(shí),如果成功,則進(jìn)入臨界區(qū)。

當(dāng)獲取鎖失敗時(shí),則創(chuàng)建一個(gè)共享類(lèi)型的節(jié)點(diǎn)并進(jìn)入一個(gè)FIFO等待隊(duì)列,然后被掛起等待喚醒。

當(dāng)隊(duì)列中的等待線程被喚醒以后就重新嘗試獲取鎖資源,如果成功則喚醒后面還在等待的共享節(jié)點(diǎn)并把該喚醒事件傳遞下去,即會(huì)依次喚醒在該節(jié)點(diǎn)后面的所有共享節(jié)點(diǎn),然后進(jìn)入臨界區(qū),否則繼續(xù)掛起等待。

釋放鎖過(guò)程:

當(dāng)線程調(diào)用releaseShared()進(jìn)行鎖資源釋放時(shí),如果釋放成功,則喚醒隊(duì)列中等待的節(jié)點(diǎn),如果有的話。

二、源碼深入分析

基于上面所說(shuō)的共享鎖執(zhí)行流程,我們接下來(lái)看下源碼實(shí)現(xiàn)邏輯:
首先來(lái)看下獲取鎖的方法acquireShared(),如下

   public final void acquireShared(int arg) {
        //嘗試獲取共享鎖,返回值小于0表示獲取失敗
        if (tryAcquireShared(arg) < 0)
            //執(zhí)行獲取鎖失敗以后的方法
            doAcquireShared(arg);
    }

這里tryAcquireShared()方法是留給用戶去實(shí)現(xiàn)具體的獲取鎖邏輯的。關(guān)于該方法的實(shí)現(xiàn)有兩點(diǎn)需要特別說(shuō)明:

一、該方法必須自己檢查當(dāng)前上下文是否支持獲取共享鎖,如果支持再進(jìn)行獲取。

二、該方法返回值是個(gè)重點(diǎn)。其一、由上面的源碼片段可以看出返回值小于0表示獲取鎖失敗,需要進(jìn)入等待隊(duì)列。其二、如果返回值等于0表示當(dāng)前線程獲取共享鎖成功,但它后續(xù)的線程是無(wú)法繼續(xù)獲取的,也就是不需要把它后面等待的節(jié)點(diǎn)喚醒。最后、如果返回值大于0,表示當(dāng)前線程獲取共享鎖成功且它后續(xù)等待的節(jié)點(diǎn)也有可能繼續(xù)獲取共享鎖成功,也就是說(shuō)此時(shí)需要把后續(xù)節(jié)點(diǎn)喚醒讓它們?nèi)L試獲取共享鎖。

有了上面的約定,我們?cè)賮?lái)看下doAcquireShared方法的實(shí)現(xiàn):

    //參數(shù)不多說(shuō),就是傳給acquireShared()的參數(shù)
    private void doAcquireShared(int arg) {
        //添加等待節(jié)點(diǎn)的方法跟獨(dú)占鎖一樣,唯一區(qū)別就是節(jié)點(diǎn)類(lèi)型變?yōu)榱斯蚕硇?,不再贅?        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //表示前面的節(jié)點(diǎn)已經(jīng)獲取到鎖,自己會(huì)嘗試獲取鎖
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    //注意上面說(shuō)的, 等于0表示不用喚醒后繼節(jié)點(diǎn),大于0需要
                    if (r >= 0) {
                        //這里是重點(diǎn),獲取到鎖以后的喚醒操作,后面詳細(xì)說(shuō)
                        setHeadAndPropagate(node, r);
                        p.next = null;
                        //如果是因?yàn)橹袛嘈褋?lái)則設(shè)置中斷標(biāo)記位
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //掛起邏輯跟獨(dú)占鎖一樣,不再贅述
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            //獲取失敗的取消邏輯跟獨(dú)占鎖一樣,不再贅述
            if (failed)
                cancelAcquire(node);
        }
    }

獨(dú)占鎖模式獲取成功以后設(shè)置頭結(jié)點(diǎn)然后返回中斷狀態(tài),結(jié)束流程。而共享鎖模式獲取成功以后,調(diào)用了setHeadAndPropagate方法,從方法名就可以看出除了設(shè)置新的頭結(jié)點(diǎn)以外還有一個(gè)傳遞動(dòng)作,一起看下代碼:

    //兩個(gè)入?yún)?,一個(gè)是當(dāng)前成功獲取共享鎖的節(jié)點(diǎn),一個(gè)就是tryAcquireShared方法的返回值,注意上面說(shuō)的,它可能大于0也可能等于0
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; //記錄當(dāng)前頭節(jié)點(diǎn)
        //設(shè)置新的頭節(jié)點(diǎn),即把當(dāng)前獲取到鎖的節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)
        //注:這里是獲取到鎖之后的操作,不需要并發(fā)控制
        setHead(node);
        //這里意思有兩種情況是需要執(zhí)行喚醒操作
        //1.propagate > 0 表示調(diào)用方指明了后繼節(jié)點(diǎn)需要被喚醒
        //2.頭節(jié)點(diǎn)后面的節(jié)點(diǎn)需要被喚醒(waitStatus<0),不論是老的頭結(jié)點(diǎn)還是新的頭結(jié)點(diǎn)
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            //如果當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)是共享類(lèi)型獲取沒(méi)有后繼節(jié)點(diǎn),則進(jìn)行喚醒
            //這里可以理解為除非明確指明不需要喚醒(后繼等待節(jié)點(diǎn)是獨(dú)占類(lèi)型),否則都要喚醒
            if (s == null || s.isShared())
                //后面詳細(xì)說(shuō)
                doReleaseShared();
        }
    }

    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

最終的喚醒操作也很復(fù)雜,專門(mén)拿出來(lái)分析一下:
注:這個(gè)喚醒操作在releaseShare()方法里也會(huì)調(diào)用。

private void doReleaseShared() {
        for (;;) {
            //喚醒操作由頭結(jié)點(diǎn)開(kāi)始,注意這里的頭節(jié)點(diǎn)已經(jīng)是上面新設(shè)置的頭結(jié)點(diǎn)了
            //其實(shí)就是喚醒上面新獲取到共享鎖的節(jié)點(diǎn)的后繼節(jié)點(diǎn)
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //表示后繼節(jié)點(diǎn)需要被喚醒
                if (ws == Node.SIGNAL) {
                    //這里需要控制并發(fā),因?yàn)槿肟谟衧etHeadAndPropagate跟release兩個(gè),避免兩次unpark
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;      
                    //執(zhí)行喚醒操作      
                    unparkSuccessor(h);
                }
                //如果后繼節(jié)點(diǎn)暫時(shí)不需要喚醒,則把當(dāng)前節(jié)點(diǎn)狀態(tài)設(shè)置為PROPAGATE確保以后可以傳遞下去
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                
            }
            //如果頭結(jié)點(diǎn)沒(méi)有發(fā)生變化,表示設(shè)置完成,退出循環(huán)
            //如果頭結(jié)點(diǎn)發(fā)生變化,比如說(shuō)其他線程獲取到了鎖,為了使自己的喚醒動(dòng)作可以傳遞,必須進(jìn)行重試
            if (h == head)                   
                break;
        }
    }

接下來(lái)看下釋放共享鎖的過(guò)程:

public final boolean releaseShared(int arg) {
        //嘗試釋放共享鎖
        if (tryReleaseShared(arg)) {
            //喚醒過(guò)程,詳情見(jiàn)上面分析
            doReleaseShared();
            return true;
        }
        return false;
    }

注:上面的setHeadAndPropagate()方法表示等待隊(duì)列中的線程成功獲取到共享鎖,這時(shí)候它需要喚醒它后面的共享節(jié)點(diǎn)(如果有),但是當(dāng)通過(guò)releaseShared()方法去釋放一個(gè)共享鎖的時(shí)候,接下來(lái)等待獨(dú)占鎖跟共享鎖的線程都可以被喚醒進(jìn)行嘗試獲取。

三、總結(jié)

跟獨(dú)占鎖相比,共享鎖的主要特征在于當(dāng)一個(gè)在等待隊(duì)列中的共享節(jié)點(diǎn)成功獲取到鎖以后(它獲取到的是共享鎖),既然是共享,那它必須要依次喚醒后面所有可以跟它一起共享當(dāng)前鎖資源的節(jié)點(diǎn),毫無(wú)疑問(wèn),這些節(jié)點(diǎn)必須也是在等待共享鎖(這是大前提,如果等待的是獨(dú)占鎖,那前面已經(jīng)有一個(gè)共享節(jié)點(diǎn)獲取鎖了,它肯定是獲取不到的)。當(dāng)共享鎖被釋放的時(shí)候,可以用讀寫(xiě)鎖為例進(jìn)行思考,當(dāng)一個(gè)讀鎖被釋放,此時(shí)不論是讀鎖還是寫(xiě)鎖都是可以競(jìng)爭(zhēng)資源的。

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

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

相關(guān)文章

  • 深入淺出AQS組件概覽

    摘要:原文地址深入淺出之獨(dú)占鎖模式深入淺出之共享鎖模式深入淺出之條件隊(duì)列前面三篇文章如果之前沒(méi)有基礎(chǔ)的話看起來(lái)會(huì)比較吃力,這篇文章說(shuō)明一下的基礎(chǔ)知識(shí),方便快速了解。當(dāng)前節(jié)點(diǎn)由于超時(shí)或者中斷被取消,節(jié)點(diǎn)進(jìn)入這個(gè)狀態(tài)以后將保持不變。 之前分析了AQS中的獨(dú)占鎖,共享鎖,條件隊(duì)列三大模塊,現(xiàn)在從結(jié)構(gòu)上來(lái)看看AQS各個(gè)組件的情況。 原文地址:http://www.jianshu.com/p/49b8...

    DDreach 評(píng)論0 收藏0
  • 深入淺出AQS條件隊(duì)列

    摘要:從上面的代碼可以看出,條件隊(duì)列是建立在鎖基礎(chǔ)上的,而且必須是獨(dú)占鎖原因后面會(huì)通過(guò)源碼分析。明天就是國(guó)慶長(zhǎng)假了,我自己也計(jì)劃出國(guó)玩一趟,散散心。提前祝廣大朋友國(guó)慶快樂(lè)。 相比于獨(dú)占鎖跟共享鎖,AbstractQueuedSynchronizer中的條件隊(duì)列可能被關(guān)注的并不是很多,但它在阻塞隊(duì)列的實(shí)現(xiàn)里起著至關(guān)重要的作用,同時(shí)如果想全面了解AQS,條件隊(duì)列也是必須要學(xué)習(xí)的。 原文地址:ht...

    VEIGHTZ 評(píng)論0 收藏0
  • 深入淺出AQS獨(dú)占模式

    摘要:獲取鎖的過(guò)程當(dāng)線程調(diào)用申請(qǐng)獲取鎖資源,如果成功,則進(jìn)入臨界區(qū)。如果隊(duì)列中有其他等待鎖資源的線程需要喚醒,則喚醒隊(duì)列中的第一個(gè)等待節(jié)點(diǎn)先入先出。釋放鎖時(shí),如果隊(duì)列中有等待的線程就進(jìn)行喚醒。 每一個(gè)Java工程師應(yīng)該都或多或少了解過(guò)AQS,我自己也是前前后后,反反復(fù)復(fù)研究了很久,看了忘,忘了再看,每次都有不一樣的體會(huì)。這次趁著寫(xiě)博客,打算重新拿出來(lái)系統(tǒng)的研究下它的源碼,總結(jié)成文章,便于以后...

    Corwien 評(píng)論0 收藏0
  • 【java并發(fā)編程實(shí)戰(zhàn)6】AQS獨(dú)占ReentrantLock實(shí)現(xiàn)

    摘要:鎖與很好的隔離使用者與實(shí)現(xiàn)者所需要關(guān)注的領(lǐng)域。那么這個(gè)就是包裝線程并且放入到隊(duì)列的過(guò)程實(shí)現(xiàn)的方法。也證實(shí)了就是獲取鎖的線程的節(jié)點(diǎn)。如果發(fā)生異常取消請(qǐng)求,也就是將當(dāng)前節(jié)點(diǎn)重隊(duì)列中移除。 前言 自從JDK1.5后,jdk新增一個(gè)并發(fā)工具包java.util.concurrent,提供了一系列的并發(fā)工具類(lèi)。而今天我們需要學(xué)習(xí)的是java.util.concurrent.lock也就是它下面的...

    sixleaves 評(píng)論0 收藏0
  • Java多線程——帶你看AQS框架源碼

    摘要:作用是存儲(chǔ)獲取鎖失敗的阻塞線程。獨(dú)占模式下,鎖是線程獨(dú)占的,而共享模式下,鎖是可以被多個(gè)線程占用的。等方法就是讓線程阻塞加入隊(duì)列喚醒線程等。該方法其實(shí)就是自旋嘗試獲取鎖或阻塞線程子類(lèi)實(shí)現(xiàn)決定。 AQS,全稱AbstractQueuedSynchronizer,是Concurrent包鎖的核心,沒(méi)有AQS就沒(méi)有Java的Concurrent包。它到底是個(gè)什么,我們來(lái)看看源碼的第一段注解是...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<