国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入淺出AQS之共享鎖模式

Berwin / 2382人閱讀

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

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

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

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

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

獲取鎖的過程:

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

當(dāng)獲取鎖失敗時,則創(chuàng)建一個共享類型的節(jié)點并進入一個FIFO等待隊列,然后被掛起等待喚醒。

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

釋放鎖過程:

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

二、源碼深入分析

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

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

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

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

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

有了上面的約定,我們再來看下doAcquireShared方法的實現(xiàn):

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

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

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

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

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

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

接下來看下釋放共享鎖的過程:

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

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

三、總結(jié)

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

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70609.html

相關(guān)文章

  • 深入淺出AQS組件概覽

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

    DDreach 評論0 收藏0
  • 深入淺出AQS條件隊列

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

    VEIGHTZ 評論0 收藏0
  • 深入淺出AQS獨占模式

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

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

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

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

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

    stackvoid 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<