日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java 通過AQS實現數據組織

瀏覽:157日期:2022-08-14 16:52:57
引言

從本篇文章開始,我們將介紹 Java AQS 的實現方式,本文先介紹 AQS 的內部數據是如何組織的,后面的文章中再分別介紹 AQS 的各個部門實現。

AQS

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

內部數據

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

Java 通過AQS實現數據組織

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

通過 CAS 修改 state 比較容易,大家應該都能理解,但是如果要通過 CAS 維護一個雙向隊列要怎么做呢?這里我們看一下 AQS 中 CLH 隊列的實現。在 AQS 中有兩個指針一個指針指向了隊列頭,一個指向了隊列尾。它們都是懶初始化的,也就是說最初都為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;

隊列中的每個節點,都是一個 Node 實例,該實例的第一個關鍵字段是 waitState,它表述了當前節點所處的狀態,通過 CAS 進行修改:

SIGNAL:表示當前節點承擔喚醒后繼節點的責任 CANCELLED:表示當前節點已經超時或者被打斷 CONDITION:表示當前節點正在 Condition 上等待(通過鎖可以創建 Condition 對象) PROPAGATE:只會設置在 head 節點上,用于表明釋放共享鎖時,需要將這個行為傳播到其他節點上,這個我們稍后詳細介紹。

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; } //...}

因為是雙向隊列,所以 Node 實例中勢必有 prev 和 next 指針,此外 Node 中還會保存與其對應的線程。最后是 nextWaiter,當一個節點對應了共享請求時,nextWaiter 指向了 Node. SHARED 而當一個節點是排他請求時,nextWaiter 默認指向了 Node. EXCLUSIVE 也就是 null。我們知道 AQS 也提供了 Condition 功能,該功能就是通過 nextWaiter 來維護在 Condition 上等待的線程。也就是說這里的 nextWaiter 在鎖的實現部分中,扮演者共享鎖和排它鎖的標志位,而在條件等待隊列中,充當鏈表的 next 指針。

同步隊列

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

當任意一種請求,要入隊時,先會構建一個 Node 實例,然后獲取當前 AQS 隊列的尾結點,如果尾結點為空,就是說隊列還沒初始化,初始化過程在后面 enq 函數中實現 這里我們先看初始化之后的情況,即 tail != null,先將當前 Node 的前向指針 prev 更新,然后通過 CAS 將尾結點修改為當前 Node,修改成功時,再更新前一個節點的后向指針 next,因為只有修改尾指針過程是原子的,所以這里會出現新插入一個節點時,之前的尾節點 previousTail 的 next 指針為null的情況,也就是說會存在短暫的正向指針和反向指針不同步的情況,不過在后面的介紹中,你會發現 AQS 很完備地避開了這種不同步帶來的風險(通過從后往前遍歷) 如果上述操作成功,則當前線程已經進入同步隊列,否則,可能存在多個線程的競爭,其他線程設置尾結點成功了,而當前線程失敗了,這時候會和尾結點未初始化一樣進入 enq 函數中。

/** * 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) {// 已經進行了初始化node.prev = pred;// CAS 修改尾節點if (compareAndSetTail(pred, node)) { // 成功之后再修改后向指針 pred.next = node; return node;} } // 循環 CAS 過程和初始化過程 enq(node); return node;}

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

整個 enq 的工作在一個循環中進行 先會檢查是否未進行初始化,是的話,就設置一個虛擬節點 Node 作為 head 和 tail,也就是說同步隊列的第一個節點并不保存實際數據,只是一個保存指針的地方 初始化完成后,通過 CAS 修改尾節點,直到修改成功為止,最后修復后向指針

/** * 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 (;;) {// 在一個循環中進行 CAS 操作Node t = tail;if (t == null) { // Must initialize if (compareAndSetHead(new Node()))tail = head;} else { node.prev = t; // CAS 修改尾節點 if (compareAndSetTail(t, node)) {// 成功之后再修改后向指針t.next = node;return t; }}

以上就是通過AQS實現數據組織的詳細內容,更多關于AQS數據組織的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲少妇诱惑| 欧美黄页在线免费观看| 日韩欧美一区二区三区免费看| 美女免费视频一区| 成人国产精品一区二区网站| 国产亚洲精品精品国产亚洲综合| 国产免费久久| 久久伊人国产| 国产伦久视频在线观看| www.com.cn成人| 婷婷六月综合| 日韩精品社区| 国产一区二区三区探花| 香蕉视频亚洲一级| 亚洲欧美网站| 国产精品一区二区精品视频观看 | 国产欧美日韩精品高清二区综合区| 亚洲啊v在线免费视频| 91成人在线网站| 国产66精品| 亚洲欧美日本视频在线观看| 国产亚洲久久| 人人精品亚洲| 日韩中文字幕| 青青青免费在线视频| 免费在线看一区| 精品精品久久| 午夜亚洲福利在线老司机| 国产精品一区高清| 亚洲欧美一区在线| 国产女人18毛片水真多18精品| 日韩国产欧美一区二区| 性欧美长视频| 国产aⅴ精品一区二区三区久久| 欧美精品一二| 久久精品国产99国产精品| 夜夜精品视频| 91亚洲国产成人久久精品| 亚洲精选91| 91精品国产福利在线观看麻豆| 日韩精品久久理论片| 99精品综合| 久久一区欧美| 日本不卡高清| 狠狠干成人综合网| 精品国产精品国产偷麻豆| 日本 国产 欧美色综合| 麻豆精品视频在线| 亚洲一二av| 亚洲精品va| 首页国产精品| 国产精品毛片视频| 亚洲日韩中文字幕一区| 久久九九精品| 国内一区二区三区| 欧美精品三级在线| 中文亚洲免费| 在线观看精品| 欧美www视频在线观看| 国产女人18毛片水真多18精品| 免费精品视频| 亚洲高清久久| 久久亚洲黄色| 欧美日一区二区三区在线观看国产免| 亚洲激情另类| 久久久久蜜桃| 欧美黄色网页| 国产精品久久久久久久久妇女| 国产日韩欧美一区二区三区| 久久一二三区| 亚洲色诱最新| 亚洲免费播放| 伊人久久亚洲影院| 欧洲毛片在线视频免费观看| 电影天堂国产精品| 9999国产精品| 麻豆mv在线观看| 精品一区二区三区在线观看视频 | 免费视频一区二区| 中文亚洲免费| 国产精品嫩草99av在线| 欧美日韩国产高清| 免费av一区| 1024精品一区二区三区| 久久毛片亚洲| 麻豆mv在线观看| 久久久久久久欧美精品| 久久精品99久久久| 91大神在线观看线路一区| 亚洲三区欧美一区国产二区| 亚洲激情国产| 美国三级日本三级久久99| 另类亚洲自拍| 少妇精品久久久一区二区三区| 中文字幕成人| 7777精品| 国产精品黄色| 久久久久久亚洲精品美女| 美女久久久久久 | 免费观看在线综合色| 中文在线一区| 一区二区91| 日本va欧美va精品发布| 国产精品亚洲人成在99www| 国产精品成人一区二区网站软件| 美腿丝袜亚洲三区| 中国字幕a在线看韩国电影| 欧美天堂视频| 91精品91| 日韩国产欧美一区二区三区| 日韩一区二区三区精品视频第3页 日韩一区二区三区免费视频 | 久久影院午夜精品| 久久婷婷丁香| 在线精品小视频| 久久都是精品| 欧美精品国产白浆久久久久| 国产成人免费| 欧美日韩少妇| 91精品尤物| 日韩欧美一区二区三区免费看| 青青久久av| 在线免费观看亚洲| 日本不卡不码高清免费观看| 久久wwww| 亚洲福利一区| 日本视频中文字幕一区二区三区| 久久久国产精品网站| 99国产精品一区二区| 蜜臀精品久久久久久蜜臀| 国产精品国码视频| 婷婷亚洲五月| 国产精品三级| 欧美成人日韩| 国产精品欧美三级在线观看| 久久久久网站| 欧美午夜三级| 久久要要av| 亚洲丝袜美腿一区| 久久久久久亚洲精品美女| 婷婷综合激情| 国产高清亚洲| 亚洲欧美日韩国产一区| 麻豆精品视频在线| 乱人伦精品视频在线观看| 麻豆91精品91久久久的内涵| 99在线观看免费视频精品观看| 国产高清亚洲| 石原莉奈在线亚洲三区| 福利在线一区| 日韩精品视频一区二区三区| 欧美羞羞视频| 国产精品久av福利在线观看| 久久中文视频| 麻豆成人91精品二区三区| 亚洲作爱视频| 美女一区网站| 国产精品一区二区美女视频免费看| 99久久www免费| 欧美a级一区二区| 亚洲欧洲美洲国产香蕉| 久久中文字幕av| 久久中文在线| 日本精品国产| 三级在线观看一区二区| se01亚洲视频| 精品亚洲免a| 日韩av中文字幕一区| 九九色在线视频| 国产精品nxnn| 日韩高清在线不卡| 模特精品在线| 激情欧美日韩一区| 97在线精品| 欧美xxxx性| 欧美日韩亚洲一区| 中文字幕日韩高清在线 | 欧美日韩色图| 中文一区一区三区高中清不卡免费| 日韩欧美美女在线观看| 9色国产精品| 影视先锋久久| 国产精品任我爽爆在线播放| 少妇精品在线| 蜜臀av在线播放一区二区三区| 成人一区而且| 欧美精品第一区| 日韩av中文字幕一区二区三区| 亚洲一区中文| 亚洲精品中文字幕乱码| 久久精品青草| 亚洲黄色中文字幕| 精品99在线| 精品高清久久| 精品国产成人| 成人午夜毛片| 国产一区二区三区91| 日本aⅴ精品一区二区三区| 伊人精品久久| 亚洲毛片在线| 日本精品一区二区三区在线观看视频|