最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程之ConcurrentLinkedQueue隊(duì)列詳情

 更新時(shí)間:2022年04月15日 15:07:41   作者:派大大大星?  
這篇文章主要介紹了Java并發(fā)編程之ConcurrentLinkedQueue隊(duì)列詳情,ConcurrentLinkedQueue?內(nèi)部的隊(duì)列使用單向鏈表方式實(shí)現(xiàn),下文更多相關(guān)內(nèi)容敘述需要的小伙伴可以參考一下

ConcurrentLinkedQueue

JDK中提供了一系列場(chǎng)景的并發(fā)安全隊(duì)列??偟膩?lái)說(shuō),按照實(shí)現(xiàn)方式的不同可分為阻塞隊(duì)列和非阻塞隊(duì)列,前者使用鎖實(shí)現(xiàn),而后則使用CAS非阻塞算法實(shí)現(xiàn)。

ConcurrentLinkedQueue 內(nèi)部的隊(duì)列使用單向鏈表方式實(shí)現(xiàn),其中有兩個(gè)volatile 類型的 Node 節(jié)點(diǎn)分別用來(lái)存放隊(duì)列的首、尾節(jié)點(diǎn)。從下面的無(wú)參構(gòu)造函數(shù)可知,默認(rèn)頭、尾節(jié)點(diǎn)都是指向 item 為null 的哨兵節(jié)點(diǎn)。新元素會(huì)被插入隊(duì)列末尾,出隊(duì)時(shí)從隊(duì)列頭部獲取一個(gè)元素。

public ConcurrentLinkedQueue() {
    head = tail = new Node<E>(null);
}

在 Node 節(jié)點(diǎn)內(nèi)部則維護(hù)一個(gè)使用volatile 修飾的變量 item,用來(lái)存放節(jié)點(diǎn)的值;next用來(lái)存放鏈表的下一個(gè)節(jié)點(diǎn),從而鏈接為一個(gè)單向無(wú)界鏈表。其內(nèi)部則使用 UNSafe 工具類提供的CAS 算法來(lái)保證出入隊(duì)時(shí)操作鏈表的原子性。

下面通過(guò)介紹ConcurrentLinkedQueue的幾個(gè)方法來(lái)介紹其實(shí)現(xiàn)原理。

offer操作: offer操作是在隊(duì)列末尾添加一個(gè)元素,如果傳遞的參數(shù)是null則拋出NPE異常,否則由于ConcurrentLinkedQueue是無(wú)界隊(duì)列,該方法一直會(huì)返回true。另外,由于使用CAS無(wú)阻塞算法,因此該方法不會(huì)阻塞掛起調(diào)用線程。下面具體看下實(shí)現(xiàn)原理。

public boolean offer(E e) {
//(1)e為null這拋出空指針異常
    checkNotNull(e);
    //(2)構(gòu)造Node節(jié)點(diǎn),在構(gòu)造函數(shù)內(nèi)部調(diào)用unsafe.putObject
    final Node<E> newNode = new Node<E>(e);
    //(3) 從尾節(jié)點(diǎn)插入
    for (Node<E> t = tail, p = t;;) {
        Node<E> q = p.next;
 //(4) 如果q==null說(shuō)明p是尾節(jié)點(diǎn),則執(zhí)行插入
        if (q == null) {
            // p is last node
            //(5)使用CAS設(shè)置p節(jié)點(diǎn)的next節(jié)點(diǎn)
            if (p.casNext(null, newNode)) {
                // Successful CAS is the linearization point
                // for e to become an element of this queue,
                // and for newNode to become "live".
                   //(6)CAS成功,則說(shuō)明新增節(jié)點(diǎn)已經(jīng)放入鏈表,然后設(shè)置當(dāng)前尾巴節(jié)點(diǎn)

                if (p != t) // hop two nodes at a time
                    casTail(t, newNode);  // Failure is OK.
                return true;
            }
            // Lost CAS race to another thread; re-read next
        }
        else if (p == q)
            // We have fallen off list.  If tail is unchanged, it
            // will also be off-list, in which case we need to
            // jump to head, from which all live nodes are always
            // reachable.  Else the new tail is a better bet.
            p = (t != (t = tail)) ? t : head;
        else
            // Check for tail updates after two hops.
            p = (p != t && t != (t = tail)) ? t : q;
    }
}
  • 首先看當(dāng)一個(gè)線程調(diào)用offer(item)時(shí)的情況。首先代碼(1)對(duì)傳參進(jìn)行空檢查, 由于使用 如果為null 則拋出NPE 異常,否則執(zhí)行代碼(2)并使用item作為構(gòu)造函數(shù)參數(shù)創(chuàng)建一 個(gè)新的節(jié)點(diǎn),然后代碼(3)從隊(duì)列尾部節(jié)點(diǎn)開始循環(huán),打算從隊(duì)列尾部添加元素。這時(shí)候節(jié)點(diǎn)p、t、head、tail同時(shí)指向了item為null的哨兵節(jié)點(diǎn),由于哨兵節(jié)點(diǎn)的next 節(jié)點(diǎn)為null,所以這里q也指向null。代碼(4)發(fā)現(xiàn)q->null則執(zhí)行代碼(5),通過(guò)CAS 原子操作判斷p節(jié)點(diǎn)的next節(jié)點(diǎn)是否為null,如果為null 則使用節(jié)點(diǎn)newNode替換p的next節(jié)點(diǎn),然后執(zhí)行代碼(6),這里由于p=t所以沒有設(shè)置尾部節(jié)點(diǎn),然后退出 offer方法。
  • 上面是一個(gè)線程調(diào)用offer方法的情況,如果多個(gè)線程同時(shí)調(diào)用,就會(huì)存在多個(gè)線程同時(shí)執(zhí)行到代碼(5)的情況。假設(shè)線程A調(diào)用offer(item1),線程B調(diào)用 ofer(item2),同時(shí)執(zhí)行到代碼(5)p.casNext(null, newNode)。由于CAS的比較設(shè)置操作是原子性的,所以這里假設(shè)線程A先執(zhí)行了比較設(shè)置操作,發(fā)現(xiàn)當(dāng)前p的 next 節(jié)點(diǎn)確實(shí)是null,則會(huì)原子性地更新next節(jié)點(diǎn)為iteml,這時(shí)候線程B也會(huì)判斷p的next節(jié)點(diǎn)是否為null,結(jié)果發(fā)現(xiàn)不是null(因?yàn)榫€程A已經(jīng)設(shè)置了p的next節(jié)點(diǎn)為iteml),則會(huì)跳到代碼(3),然后執(zhí)行到代碼(4)。

可見,offer 操作中的關(guān)鍵步驟是代碼(5),通過(guò)原子CAS 操作來(lái)控制某時(shí)只有一個(gè)線程可以追加元素到隊(duì)列末尾。進(jìn)行CAS 競(jìng)爭(zhēng)失敗的線程會(huì)通過(guò)循環(huán)一次次嘗試進(jìn)行 CAS操作,直到CAS 成功才會(huì)返回,也就是通過(guò)使用無(wú)限循環(huán)不斷進(jìn)行 CAS 嘗試方式來(lái)替代阻塞算法掛起調(diào)用線程。相比阻塞算法,這是使用CPU資源換取阻塞所帶來(lái)的開銷。

add操作:

add操作是在鏈表尾部添加一個(gè)元素,其實(shí)在內(nèi)部調(diào)用的還是offer操作。

public boolean add(E e) {
    return offer(e);
}

poll操作:

poll操作是在隊(duì)列頭部獲取并移除一個(gè)元素,如果隊(duì)列為空則返回null。

public E poll() {
    restartFromHead:
    for (;;) {
        for (Node<E> h = head, p = h, q;;) {
            E item = p.item;

            if (item != null && p.casItem(item, null)) {
                // Successful CAS is the linearization point
                // for item to be removed from this queue.
                if (p != h) // hop two nodes at a time
                    updateHead(h, ((q = p.next) != null) ? q : p);
                return item;
            }
            else if ((q = p.next) == null) {
                updateHead(h, p);
                return null;
            }
            else if (p == q)
                continue restartFromHead;
            else
                p = q;
        }
    }
}

poll方法在移除一個(gè)元素時(shí),只是簡(jiǎn)單地使用 CAS操作把當(dāng)前節(jié)點(diǎn)的item值設(shè)置為null,然后通過(guò)重新設(shè)置頭節(jié)點(diǎn)將該元素從隊(duì)列里面移除,被移除的節(jié)點(diǎn)就成了孤立節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)會(huì)在垃圾回收時(shí)被回收掉。另外,如果在執(zhí)行分支中發(fā)現(xiàn)頭節(jié)點(diǎn)被修改了,要跳到外層循環(huán)重新獲取新的頭節(jié)點(diǎn)。

peak操作:

peak操作是獲取隊(duì)列頭部獲一個(gè)元素,如果隊(duì)列為空則返回null。

public E peek() {
    restartFromHead:
    for (;;) {
        for (Node<E> h = head, p = h, q;;) {
            E item = p.item;
            
            //注釋
            if (item != null || (q = p.next) == null) {
                updateHead(h, p);
                return item;
            }
            else if (p == q)
                continue restartFromHead;
            else
                p = q;
        }
    }
}

Peek操作的代碼結(jié)構(gòu)與poll操作類似,不同之處在于我們?cè)诖a中標(biāo)記注釋的地方中少了castItem操作。其實(shí)這很正常,因?yàn)閜eek只是獲取隊(duì)列頭元素值,并不清空其值。根據(jù)前面的介紹我們知道第一次執(zhí)行offer后head指向的是哨兵節(jié)點(diǎn)(也就是item為null的節(jié)點(diǎn)),那么第一次執(zhí)行peek時(shí)在注釋處會(huì)發(fā)現(xiàn)item==null,然后執(zhí)行q=p.next,這時(shí)候q節(jié)點(diǎn)指向的才是隊(duì)列里面第一個(gè)真正的元素,或者如果隊(duì)列為 null 則 q 指向 null。

到此這篇關(guān)于Java并發(fā)編程之ConcurrentLinkedQueue隊(duì)列詳情的文章就介紹到這了,更多相關(guān)Java并發(fā)編程 ConcurrentLinkedQueue 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot定制JSON響應(yīng)數(shù)據(jù)返回的示例代碼

    SpringBoot定制JSON響應(yīng)數(shù)據(jù)返回的示例代碼

    @JsonView 是 Jackson 庫(kù)中的一個(gè)注解,它允許你定義哪些屬性應(yīng)該被序列化到 JSON 中,基于不同的“視圖”或“配置”,在本文中,通過(guò)了解@JsonView,你將能夠更好地掌握如何在Spring Boot應(yīng)用中定制JSON數(shù)據(jù)的輸出,需要的朋友可以參考下
    2024-05-05
  • Java 高并發(fā)四:無(wú)鎖詳細(xì)介紹

    Java 高并發(fā)四:無(wú)鎖詳細(xì)介紹

    本文主要介紹Java 高并發(fā)無(wú)鎖的知識(shí),這里整理了 1.無(wú)鎖類的原理詳解 2.無(wú)鎖類的使用的知識(shí),并講解其原理,有需要的小伙伴可以參考下
    2016-09-09
  • JAVA?IDEA項(xiàng)目打包為jar包的步驟詳解

    JAVA?IDEA項(xiàng)目打包為jar包的步驟詳解

    在Java開發(fā)中我們通常會(huì)將我們的項(xiàng)目打包成可執(zhí)行的Jar包,以便于在其他環(huán)境中部署和運(yùn)行,下面這篇文章主要給大家介紹了關(guān)于JAVA?IDEA項(xiàng)目打包為jar包的相關(guān)資料,需要的朋友可以參考下
    2024-08-08
  • spring學(xué)習(xí)之@SessionAttributes實(shí)例解析

    spring學(xué)習(xí)之@SessionAttributes實(shí)例解析

    這篇文章主要介紹了spring學(xué)習(xí)之@SessionAttributes實(shí)例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng)的示例代碼

    Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng)的示例代碼

    這篇文章主要介紹了如何利用Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng),可以實(shí)現(xiàn)存款,取款,查詢等功能,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-09-09
  • Springboot 項(xiàng)目讀取Resources目錄下的文件(推薦)

    Springboot 項(xiàng)目讀取Resources目錄下的文件(推薦)

    這篇文章主要介紹了Springboot 項(xiàng)目讀取Resources目錄下的文件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等

    Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等

    單鏈表是鏈表的其中一種基本結(jié)構(gòu)。一個(gè)最簡(jiǎn)單的結(jié)點(diǎn)結(jié)構(gòu)如圖所示,它是構(gòu)成單鏈表的基本結(jié)點(diǎn)結(jié)構(gòu)。在結(jié)點(diǎn)中數(shù)據(jù)域用來(lái)存儲(chǔ)數(shù)據(jù)元素,指針域用于指向下一個(gè)具有相同結(jié)構(gòu)的結(jié)點(diǎn)。 因?yàn)橹挥幸粋€(gè)指針結(jié)點(diǎn),稱為單鏈表
    2021-10-10
  • 親手教你SpringBoot中的多數(shù)據(jù)源集成問(wèn)題

    親手教你SpringBoot中的多數(shù)據(jù)源集成問(wèn)題

    本文主要是介紹基于springboot的多數(shù)據(jù)源切換,輕量級(jí)的一種集成方案,對(duì)于小型的應(yīng)用可以采用這種方案,我之前在項(xiàng)目中用到是因?yàn)楹?jiǎn)單,便于擴(kuò)展以及優(yōu)化,對(duì)SpringBoot多數(shù)據(jù)源集成問(wèn)題感興趣的朋友一起看看吧
    2022-03-03
  • java實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳下載功能

    java實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳下載功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)文件斷點(diǎn)續(xù)傳下載功能的具體代碼,感興趣的小伙伴們可以參考一下
    2016-05-05
  • MyBatis存儲(chǔ)過(guò)程、MyBatis分頁(yè)、MyBatis一對(duì)多增刪改查操作

    MyBatis存儲(chǔ)過(guò)程、MyBatis分頁(yè)、MyBatis一對(duì)多增刪改查操作

    本文通過(guò)一段代碼給大家介紹了MyBatis存儲(chǔ)過(guò)程、MyBatis分頁(yè)、MyBatis一對(duì)多增刪改查操作,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧
    2016-11-11

最新評(píng)論

象山县| 洪湖市| 晋城| 宁德市| 平凉市| 乌拉特中旗| 海阳市| 荥阳市| 芦溪县| 大余县| 永顺县| 竹溪县| 嘉禾县| 吉隆县| 抚州市| 远安县| 漳浦县| 凤阳县| 伊金霍洛旗| 萍乡市| 孟州市| 于都县| 封丘县| 卓资县| 四平市| 嘉峪关市| 轮台县| 海城市| 马山县| 阿克| 安多县| 始兴县| 清水县| 庄河市| 贡山| 榆林市| 壶关县| 惠来县| 宜兰市| 勐海县| 沂南县|