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

Java 通過AQS實(shí)現(xiàn)數(shù)據(jù)組織

 更新時(shí)間:2021年04月08日 17:17:05   作者:貝克街的流浪貓  
這篇文章主要介紹了通過AQS實(shí)現(xiàn)數(shù)據(jù)組織,想了解AQS的同學(xué)可以參考下

引言

從本篇文章開始,我們將介紹 Java AQS 的實(shí)現(xiàn)方式,本文先介紹 AQS 的內(nèi)部數(shù)據(jù)是如何組織的,后面的文章中再分別介紹 AQS 的各個(gè)部門實(shí)現(xiàn)。

AQS

通過前面的介紹,大家一定看出來了,上述的各種類型的鎖和一些線程控制接口(CountDownLatch 等),最終都是通過 AQS 來實(shí)現(xiàn)的,不同之處只在于 tryAcquire 等抽象函數(shù)如何實(shí)現(xiàn)。從這個(gè)角度來看,AQS(AbstractQueuedSynchronizer) 這個(gè)基類設(shè)計(jì)的真的很不錯(cuò),能夠包容各種同步控制方案,并提供了必須的下層依賴:比如阻塞,隊(duì)列等。接下來我們就來揭開它神秘的面紗。

內(nèi)部數(shù)據(jù)

AQS 顧名思義,就是通過隊(duì)列來組織修改互斥資源的請求。當(dāng)這個(gè)資源空閑時(shí)間,那么修改請求可以直接進(jìn)行,而當(dāng)這個(gè)資源處于鎖定狀態(tài)時(shí),就需要等待,AQS 會(huì)將所有等待的請求維護(hù)在一個(gè)類似于 CLH 的隊(duì)列中。CLH:Craig、Landin and Hagersten隊(duì)列,是單向鏈表,AQS中的隊(duì)列是CLH變體的虛擬雙向隊(duì)列(FIFO),AQS是通過將每條請求共享資源的線程封裝成一個(gè)節(jié)點(diǎn)來實(shí)現(xiàn)鎖的分配。主要原理圖如下:

圖中的 state 是一個(gè)用 volatile 修飾的 int 變量,它的使用都是通過 CAS 來進(jìn)行的,而 FIFO 隊(duì)列完成請求排隊(duì)的工作,隊(duì)列的操作也是通過 CAS 來進(jìn)行的,正因如此該隊(duì)列的操作才能達(dá)到理想的性能要求。

通過 CAS 修改 state 比較容易,大家應(yīng)該都能理解,但是如果要通過 CAS 維護(hù)一個(gè)雙向隊(duì)列要怎么做呢?這里我們看一下 AQS 中 CLH 隊(duì)列的實(shí)現(xiàn)。在 AQS 中有兩個(gè)指針一個(gè)指針指向了隊(duì)列頭,一個(gè)指向了隊(duì)列尾。它們都是懶初始化的,也就是說最初都為null。

/**
 * Head of the wait queue, lazily initialized.  Except for
 * initialization, it is modified only via method setHead.  Note:
 * If head exists, its waitStatus is guaranteed not to be
 * CANCELLED.
 */
private transient volatile Node head;

/**
 * Tail of the wait queue, lazily initialized.  Modified only via
 * method enq to add new wait node.
 */
private transient volatile Node tail;

隊(duì)列中的每個(gè)節(jié)點(diǎn),都是一個(gè) Node 實(shí)例,該實(shí)例的第一個(gè)關(guān)鍵字段是 waitState,它表述了當(dāng)前節(jié)點(diǎn)所處的狀態(tài),通過 CAS 進(jìn)行修改:

  • SIGNAL:表示當(dāng)前節(jié)點(diǎn)承擔(dān)喚醒后繼節(jié)點(diǎn)的責(zé)任
  • CANCELLED:表示當(dāng)前節(jié)點(diǎn)已經(jīng)超時(shí)或者被打斷
  • CONDITION:表示當(dāng)前節(jié)點(diǎn)正在 Condition 上等待(通過鎖可以創(chuàng)建 Condition 對象)
  • PROPAGATE:只會(huì)設(shè)置在 head 節(jié)點(diǎn)上,用于表明釋放共享鎖時(shí),需要將這個(gè)行為傳播到其他節(jié)點(diǎn)上,這個(gè)我們稍后詳細(xì)介紹。
static final class Node {
    /** Marker to indicate a node is waiting in shared mode */
    static final Node SHARED = new Node();
    /** Marker to indicate a node is waiting in exclusive mode */
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled */
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;

    /**
     * Status field, taking on only the values:
     *   SIGNAL:     The successor of this node is (or will soon be)
     *               blocked (via park), so the current node must
     *               unpark its successor when it releases or
     *               cancels. To avoid races, acquire methods must
     *               first indicate they need a signal,
     *               then retry the atomic acquire, and then,
     *               on failure, block.
     *   CANCELLED:  This node is cancelled due to timeout or interrupt.
     *               Nodes never leave this state. In particular,
     *               a thread with cancelled node never again blocks.
     *   CONDITION:  This node is currently on a condition queue.
     *               It will not be used as a sync queue node
     *               until transferred, at which time the status
     *               will be set to 0. (Use of this value here has
     *               nothing to do with the other uses of the
     *               field, but simplifies mechanics.)
     *   PROPAGATE:  A releaseShared should be propagated to other
     *               nodes. This is set (for head node only) in
     *               doReleaseShared to ensure propagation
     *               continues, even if other operations have
     *               since intervened.
     *   0:          None of the above
     *
     * The values are arranged numerically to simplify use.
     * Non-negative values mean that a node doesn't need to
     * signal. So, most code doesn't need to check for particular
     * values, just for sign.
     *
     * The field is initialized to 0 for normal sync nodes, and
     * CONDITION for condition nodes.  It is modified using CAS
     * (or when possible, unconditional volatile writes).
     */
    volatile int waitStatus;

    /**
     * Link to predecessor node that current node/thread relies on
     * for checking waitStatus. Assigned during enqueuing, and nulled
     * out (for sake of GC) only upon dequeuing.  Also, upon
     * cancellation of a predecessor, we short-circuit while
     * finding a non-cancelled one, which will always exist
     * because the head node is never cancelled: A node becomes
     * head only as a result of successful acquire. A
     * cancelled thread never succeeds in acquiring, and a thread only
     * cancels itself, not any other node.
     */
    volatile Node prev;

    /**
     * Link to the successor node that the current node/thread
     * unparks upon release. Assigned during enqueuing, adjusted
     * when bypassing cancelled predecessors, and nulled out (for
     * sake of GC) when dequeued.  The enq operation does not
     * assign next field of a predecessor until after attachment,
     * so seeing a null next field does not necessarily mean that
     * node is at end of queue. However, if a next field appears
     * to be null, we can scan prev's from the tail to
     * double-check.  The next field of cancelled nodes is set to
     * point to the node itself instead of null, to make life
     * easier for isOnSyncQueue.
     */
    volatile Node next;

    /**
     * The thread that enqueued this node.  Initialized on
     * construction and nulled out after use.
     */
    volatile Thread thread;

    /**
     * Link to next node waiting on condition, or the special
     * value SHARED.  Because condition queues are accessed only
     * when holding in exclusive mode, we just need a simple
     * linked queue to hold nodes while they are waiting on
     * conditions. They are then transferred to the queue to
     * re-acquire. And because conditions can only be exclusive,
     * we save a field by using special value to indicate shared
     * mode.
     */
    Node nextWaiter;

    /**
     * Returns true if node is waiting in shared mode.
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    //...
}

因?yàn)槭请p向隊(duì)列,所以 Node 實(shí)例中勢必有 prev 和 next 指針,此外 Node 中還會(huì)保存與其對應(yīng)的線程。最后是 nextWaiter,當(dāng)一個(gè)節(jié)點(diǎn)對應(yīng)了共享請求時(shí),nextWaiter 指向了 Node. SHARED 而當(dāng)一個(gè)節(jié)點(diǎn)是排他請求時(shí),nextWaiter 默認(rèn)指向了 Node. EXCLUSIVE 也就是 null。我們知道 AQS 也提供了 Condition 功能,該功能就是通過 nextWaiter 來維護(hù)在 Condition 上等待的線程。也就是說這里的 nextWaiter 在鎖的實(shí)現(xiàn)部分中,扮演者共享鎖和排它鎖的標(biāo)志位,而在條件等待隊(duì)列中,充當(dāng)鏈表的 next 指針。

同步隊(duì)列

接下來,我們由最常見的入隊(duì)操作出發(fā),介紹 AQS 框架的實(shí)現(xiàn)與使用。從下面的代碼中可以看到入隊(duì)操作支持兩種模式,一種是排他模式,一種是共享模式,分別對應(yīng)了排它鎖場景和共享鎖場景。

  1. 當(dāng)任意一種請求,要入隊(duì)時(shí),先會(huì)構(gòu)建一個(gè) Node 實(shí)例,然后獲取當(dāng)前 AQS 隊(duì)列的尾結(jié)點(diǎn),如果尾結(jié)點(diǎn)為空,就是說隊(duì)列還沒初始化,初始化過程在后面 enq 函數(shù)中實(shí)現(xiàn)
  2. 這里我們先看初始化之后的情況,即 tail != null,先將當(dāng)前 Node 的前向指針 prev 更新,然后通過 CAS 將尾結(jié)點(diǎn)修改為當(dāng)前 Node,修改成功時(shí),再更新前一個(gè)節(jié)點(diǎn)的后向指針 next,因?yàn)橹挥行薷奈仓羔樳^程是原子的,所以這里會(huì)出現(xiàn)新插入一個(gè)節(jié)點(diǎn)時(shí),之前的尾節(jié)點(diǎn) previousTail 的 next 指針為null的情況,也就是說會(huì)存在短暫的正向指針和反向指針不同步的情況,不過在后面的介紹中,你會(huì)發(fā)現(xiàn) AQS 很完備地避開了這種不同步帶來的風(fēng)險(xiǎn)(通過從后往前遍歷)
  3. 如果上述操作成功,則當(dāng)前線程已經(jīng)進(jìn)入同步隊(duì)列,否則,可能存在多個(gè)線程的競爭,其他線程設(shè)置尾結(jié)點(diǎn)成功了,而當(dāng)前線程失敗了,這時(shí)候會(huì)和尾結(jié)點(diǎn)未初始化一樣進(jìn)入 enq 函數(shù)中。
/**
 * Creates and enqueues node for current thread and given mode.
 *
 * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
 * @return the new node
 */
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) {
        // 已經(jīng)進(jìn)行了初始化
        node.prev = pred;
        // CAS 修改尾節(jié)點(diǎn)
        if (compareAndSetTail(pred, node)) {
            // 成功之后再修改后向指針
            pred.next = node;
            return node;
        }
    }
    // 循環(huán) CAS 過程和初始化過程
    enq(node);
    return node;
}

正常通過 CAS 修改數(shù)據(jù)都會(huì)在一個(gè)循環(huán)中進(jìn)行,而這里的 addWaiter 只是在一個(gè) if 中進(jìn)行,這是為什么呢?實(shí)際上,大家看到的 addWaiter 的這部分 CAS 過程是一個(gè)快速執(zhí)行線,在沒有競爭時(shí),這種方式能省略不少判斷過程。當(dāng)發(fā)生競爭時(shí),會(huì)進(jìn)入 enq 函數(shù)中,那里才是循環(huán) CAS 的地方。

  1. 整個(gè) enq 的工作在一個(gè)循環(huán)中進(jìn)行
  2. 先會(huì)檢查是否未進(jìn)行初始化,是的話,就設(shè)置一個(gè)虛擬節(jié)點(diǎn) Node 作為 head 和 tail,也就是說同步隊(duì)列的第一個(gè)節(jié)點(diǎn)并不保存實(shí)際數(shù)據(jù),只是一個(gè)保存指針的地方
  3. 初始化完成后,通過 CAS 修改尾節(jié)點(diǎn),直到修改成功為止,最后修復(fù)后向指針
/**
 * Inserts node into queue, initializing if necessary. See picture above.
 * @param node the node to insert
 * @return node's predecessor
 */
private Node enq(final Node node) {
    for (;;) {// 在一個(gè)循環(huán)中進(jìn)行 CAS 操作
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            // CAS 修改尾節(jié)點(diǎn)
            if (compareAndSetTail(t, node)) {
                // 成功之后再修改后向指針
                t.next = node;
                return t;
            }
        }

以上就是通過AQS實(shí)現(xiàn)數(shù)據(jù)組織的詳細(xì)內(nèi)容,更多關(guān)于AQS數(shù)據(jù)組織的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringMVC使用@ExceptionHandler注解在Controller中處理異常

    SpringMVC使用@ExceptionHandler注解在Controller中處理異常

    這篇文章主要為大家介紹了SpringMVC使用@ExceptionHandler注解在Controller中處理異常示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法

    SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法

    Spring Boot Actuator 是開發(fā)和管理生產(chǎn)級(jí) Spring Boot 應(yīng)用程序的重要工具,它可以幫助你確保應(yīng)用程序的穩(wěn)定性和性能,本文給大家介紹了SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法,需要的朋友可以參考下
    2024-05-05
  • Maven依賴管理之parent與dependencyManagement深入分析

    Maven依賴管理之parent與dependencyManagement深入分析

    首先我們來說說parent標(biāo)簽,其實(shí)這個(gè)不難解釋,就是父的意思,pom也有繼承的。比方說我現(xiàn)在有A,B,C,A是B,C的父級(jí)。現(xiàn)在就是有一個(gè)情況B,C其實(shí)有很多jar都是共同的,其實(shí)是可以放在父項(xiàng)目里面,這樣,讓B,C都繼承A就方便管理了
    2022-10-10
  • maven?插件?assembly?打tar.gz包的詳細(xì)過程

    maven?插件?assembly?打tar.gz包的詳細(xì)過程

    這篇文章主要介紹了maven插件assembly打tar.gz包的詳細(xì)過程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢實(shí)例詳解

    mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢實(shí)例詳解

    MyBatis-Plus是一個(gè)MyBatis(opens new window)的增強(qiáng)工具,在 MyBatis的基礎(chǔ)上只做增強(qiáng)不做改變,MyBatis可以無損升級(jí)為MyBatis-Plus,這篇文章主要給大家介紹了關(guān)于mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • SpringBoot實(shí)現(xiàn)Read Through模式的操作過程

    SpringBoot實(shí)現(xiàn)Read Through模式的操作過程

    Read Through模式通常是指一種緩存策略,其中當(dāng)應(yīng)用程序嘗試讀取數(shù)據(jù)時(shí),緩存系統(tǒng)首先被檢查以查看數(shù)據(jù)是否已經(jīng)存在于緩存中,這篇文章主要介紹了SpringBoot實(shí)現(xiàn)Read Through模式,需要的朋友可以參考下
    2024-07-07
  • java實(shí)現(xiàn)PDF轉(zhuǎn)圖片的方法

    java實(shí)現(xiàn)PDF轉(zhuǎn)圖片的方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)PDF轉(zhuǎn)圖片的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • java線性表排序示例分享

    java線性表排序示例分享

    這篇文章主要介紹了java線性表排序示例,需要的朋友可以參考下
    2014-03-03
  • SpringBoot詳細(xì)講解如何創(chuàng)建及刷新Spring容器bean

    SpringBoot詳細(xì)講解如何創(chuàng)建及刷新Spring容器bean

    前面看spring源碼時(shí)可以發(fā)現(xiàn)refresh()方法十分重要。在這個(gè)方法中會(huì)加載beanDefinition,同時(shí)創(chuàng)建bean對象。那么在springboot中有沒有使用這個(gè)refresh()方法呢
    2022-06-06
  • Java 詳細(xì)講解線程安全與同步附實(shí)例與注釋

    Java 詳細(xì)講解線程安全與同步附實(shí)例與注釋

    線程安全是多線程編程時(shí)的計(jì)算機(jī)程序代碼中的一個(gè)概念。在擁有共享數(shù)據(jù)的多條線程并行執(zhí)行的程序中,線程安全的代碼會(huì)通過同步機(jī)制保證各個(gè)線程都可以正常且正確的執(zhí)行,不會(huì)出現(xiàn)數(shù)據(jù)污染等意外情況
    2022-04-04

最新評論

苍山县| 合水县| 兴宁市| 浪卡子县| 武清区| 延安市| 安康市| 城市| 青川县| 乌兰浩特市| 修水县| 长治县| 桂东县| 满城县| 浦北县| 咸阳市| 锦州市| 邹平县| 普格县| 墨玉县| 石柱| 老河口市| 兴和县| 凤凰县| 赫章县| 德庆县| 兰西县| 淄博市| 武隆县| 三台县| 尼木县| 丽江市| 荣昌县| 杭州市| 长海县| 沁水县| 哈巴河县| 朝阳市| 马关县| 广东省| 杭锦后旗|