成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

靈魂拷問,AQS 是個(gè)啥???

開發(fā) 開發(fā)工具
AQS 隊(duì)列在內(nèi)部維護(hù)了一個(gè) FIFO 的雙向鏈表,如果對(duì)數(shù)據(jù)結(jié)構(gòu)比較熟的話,應(yīng)該很容易就能想到,在雙向鏈表中,每個(gè)節(jié)點(diǎn)都有兩個(gè)指針,分別指向直接前驅(qū)節(jié)點(diǎn)和直接后繼節(jié)點(diǎn)。

 啥?你連 AQS 是啥都不知道?

如果想要精通 Java 并發(fā)的話, AQS 是一定要掌握的。今天跟著阿粉一起搞一搞。

[[328494]]

基本概念

AQS 是 AbstractQueuedSynchronizer 的簡(jiǎn)稱,翻譯成中文就是 抽象隊(duì)列同步器 ,這三個(gè)單詞分開來看:

  • Abstract (抽象):也就是說, AQS 是一個(gè)抽象類,只實(shí)現(xiàn)一些主要的邏輯,有些方法推遲到子類實(shí)現(xiàn)
  • Queued (隊(duì)列):隊(duì)列有啥特征呢?先進(jìn)先出( FIFO )對(duì)吧?也就是說, AQS 是用先進(jìn)先出隊(duì)列來存儲(chǔ)數(shù)據(jù)的
  • Synchronizer (同步):即 AQS 實(shí)現(xiàn)同步功能

以上概括一下, AQS 是一個(gè)用來構(gòu)建鎖和同步器的框架,使用 AQS 能簡(jiǎn)單而又高效地構(gòu)造出同步器。

AQS 內(nèi)部實(shí)現(xiàn)

AQS 隊(duì)列在內(nèi)部維護(hù)了一個(gè) FIFO 的雙向鏈表,如果對(duì)數(shù)據(jù)結(jié)構(gòu)比較熟的話,應(yīng)該很容易就能想到,在雙向鏈表中,每個(gè)節(jié)點(diǎn)都有兩個(gè)指針,分別指向直接前驅(qū)節(jié)點(diǎn)和直接后繼節(jié)點(diǎn)。使用雙向鏈表的優(yōu)點(diǎn)之一,就是從任意一個(gè)節(jié)點(diǎn)開始都很容易訪問它的前驅(qū)節(jié)點(diǎn)和后繼節(jié)點(diǎn)。

在 AQS 中,每個(gè) Node 其實(shí)就是一個(gè)線程封裝,當(dāng)線程在競(jìng)爭(zhēng)鎖失敗之后,會(huì)封裝成 Node 加入到 AQS 隊(duì)列中;獲取鎖的線程釋放鎖之后,會(huì)從隊(duì)列中喚醒一個(gè)阻塞的 Node (也就是線程)

AQS 使用 volatile 的變量 state 來作為資源的標(biāo)識(shí):

  1. private volatile int state; 

關(guān)于 state 狀態(tài)的讀取與修改,子類可以通過覆蓋 getState() 和 setState() 方法來實(shí)現(xiàn)自己的邏輯,其中比較重要的是:

  1. // 傳入期望值 expect ,想要修改的值 update ,然后通過 Unsafe 的 compareAndSwapInt() 即 CAS 操作來實(shí)現(xiàn) 
  2. protected final boolean compareAndSetState(int expect, int update) { 
  3.     // See below for intrinsics setup to support this 
  4.     return unsafe.compareAndSwapInt(this, stateOffset, expect, update); 

下面是 AQS 中兩個(gè)重要的成員變量:

  1. private transient volatile Node head;   // 頭結(jié)點(diǎn) 
  2. private transient volatile Node tail;   // 尾節(jié)點(diǎn) 

關(guān)于 AQS 維護(hù)的雙向鏈表,在源碼中是這樣解釋的:

  • The wait queue is a variant of a "CLH" (Craig, Landin, and Hagersten) lock queue. CLH locks are normally used for spinlocks. We instead use them for blocking synchronizers, but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node.

也就是 AQS 的等待隊(duì)列是 “CLH” 鎖定隊(duì)列的變體

直接來一張圖會(huì)更形象一些:

Node 節(jié)點(diǎn)維護(hù)的是線程,控制線程的一些操作,具體來看看是 Node 是怎么做的:

  1. static final class Node { 
  2.     /** Marker to indicate a node is waiting in shared mode */ 
  3.     // 標(biāo)記一個(gè)節(jié)點(diǎn),在 共享模式 下等待 
  4.     static final Node SHARED = new Node(); 
  5.   
  6.     /** Marker to indicate a node is waiting in exclusive mode */ 
  7.     // 標(biāo)記一個(gè)節(jié)點(diǎn),在 獨(dú)占模式 下等待 
  8.     static final Node EXCLUSIVE = null
  9.  
  10.     /** waitStatus value to indicate thread has cancelled */ 
  11.     // waitStatus 的值,表示該節(jié)點(diǎn)從隊(duì)列中取消 
  12.     static final int CANCELLED =  1; 
  13.   
  14.     /** waitStatus value to indicate successor's thread needs unparking */ 
  15.     // waitStatus 的值,表示后繼節(jié)點(diǎn)在等待喚醒 
  16.     // 只有處于 signal 狀態(tài)的節(jié)點(diǎn),才能被喚醒 
  17.     static final int SIGNAL    = -1; 
  18.   
  19.     /** waitStatus value to indicate thread is waiting on condition */ 
  20.     // waitStatus 的值,表示該節(jié)點(diǎn)在等待一些條件 
  21.     static final int CONDITION = -2; 
  22.   
  23.  /** 
  24.      * waitStatus value to indicate the next acquireShared should 
  25.      * unconditionally propagate 
  26.     */ 
  27.     // waitStatus 的值,表示有資源可以使用,新 head 節(jié)點(diǎn)需要喚醒后繼節(jié)點(diǎn) 
  28.     // 如果是在共享模式下,同步狀態(tài)應(yīng)該無條件傳播下去 
  29.     static final int PROPAGATE = -3; 
  30.  
  31.  // 節(jié)點(diǎn)狀態(tài),取值為 -3,-2,-1,0,1 
  32.     volatile int waitStatus; 
  33.  
  34.  // 前驅(qū)節(jié)點(diǎn) 
  35.     volatile Node prev; 
  36.  
  37.  // 后繼節(jié)點(diǎn) 
  38.     volatile Node next
  39.  
  40.  // 節(jié)點(diǎn)所對(duì)應(yīng)的線程 
  41.     volatile Thread thread; 
  42.  
  43.  // condition 隊(duì)列中的后繼節(jié)點(diǎn) 
  44.     Node nextWaiter; 
  45.  
  46.  // 判斷是否是共享模式 
  47.     final boolean isShared() { 
  48.         return nextWaiter == SHARED; 
  49.     } 
  50.  
  51.  /** 
  52.  * 返回前驅(qū)節(jié)點(diǎn) 
  53.  */ 
  54.     final Node predecessor() throws NullPointerException { 
  55.         Node p = prev; 
  56.         if (p == null
  57.             throw new NullPointerException(); 
  58.         else 
  59.             return p; 
  60.     } 
  61.  
  62.     Node() {    // Used to establish initial head or SHARED marker 
  63.     } 
  64.  
  65.  /** 
  66.  * 將線程構(gòu)造成一個(gè) Node 節(jié)點(diǎn),然后添加到 condition 隊(duì)列中 
  67.  */ 
  68.     Node(Thread thread, Node mode) {     // Used by addWaiter 
  69.         this.nextWaiter = mode; 
  70.         this.thread = thread; 
  71.     } 
  72.  
  73.  /** 
  74.  * 等待隊(duì)列用到的方法 
  75.  */ 
  76.     Node(Thread thread, int waitStatus) { // Used by Condition 
  77.         this.waitStatus = waitStatus; 
  78.         this.thread = thread; 
  79.     } 

AQS 如何獲取資源

在 AQS 中,獲取資源的入口是 acquire(int arg) 方法,其中 arg 是獲取資源的個(gè)數(shù),來看下代碼:

  1. public final void acquire(int arg) { 
  2.     if (!tryAcquire(arg) && 
  3.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
  4.         selfInterrupt(); 

在獲取資源時(shí),會(huì)首先調(diào)用 tryAcquire 方法,這個(gè)方法是在子類中具體實(shí)現(xiàn)的

如果通過 tryAcquire 獲取資源失敗,接下來會(huì)通過 addWaiter(Node.EXCLUSIVE) 方法,將這個(gè)線程插入到等待隊(duì)列中,具體代碼:

  1. private Node addWaiter(Node mode) { 
  2.  // 生成該線程所對(duì)應(yīng)的 Node 節(jié)點(diǎn) 
  3.     Node node = new Node(Thread.currentThread(), mode); 
  4.     // 將 Node 插入到隊(duì)列中 
  5.     Node pred = tail; 
  6.     if (pred != null) { 
  7.         node.prev = pred; 
  8.         // 使用 CAS 操作,如果成功就返回 
  9.         if (compareAndSetTail(pred, node)) { 
  10.             pred.next = node; 
  11.             return node; 
  12.         } 
  13.     } 
  14.     // 如果 pred == null 或者 CAS 操作失敗,則調(diào)用 enq 方法再次自旋插入 
  15.     enq(node); 
  16.     return node; 
  17.   
  18. // 自旋 CAS 插入等待隊(duì)列 
  19. private Node enq(final Node node) { 
  20.     for (;;) { 
  21.         Node t = tail; 
  22.         if (t == null) { // Must initialize 
  23.             if (compareAndSetHead(new Node())) 
  24.                 tail = head; 
  25.         } else { 
  26.             node.prev = t; 
  27.             if (compareAndSetTail(t, node)) { 
  28.                 t.next = node; 
  29.                 return t; 
  30.             } 
  31.         } 
  32.     } 

在上面能夠看到使用的是 CAS 自旋插入,這是因?yàn)樵?AQS 中會(huì)存在多個(gè)線程同時(shí)競(jìng)爭(zhēng)資源的情況,進(jìn)而一定會(huì)出現(xiàn)多個(gè)線程同時(shí)插入節(jié)點(diǎn)的操作,這里使用 CAS 自旋插入是為了保證操作的線程安全性

現(xiàn)在呢,申請(qǐng) acquire(int arg) 方法,然后通過調(diào)用 addWaiter 方法,將一個(gè) Node 插入到了隊(duì)列尾部。處于等待隊(duì)列節(jié)點(diǎn)是從頭結(jié)點(diǎn)開始一個(gè)一個(gè)的去獲取資源,獲取資源方式如下:

  1. final boolean acquireQueued(final Node node, int arg) { 
  2.     boolean failed = true
  3.     try { 
  4.         boolean interrupted = false
  5.         for (;;) { 
  6.             final Node p = node.predecessor(); 
  7.             // 如果 Node 的前驅(qū)節(jié)點(diǎn) p 是 head,說明 Node 是第二個(gè)節(jié)點(diǎn),那么它就可以嘗試獲取資源 
  8.             if (p == head && tryAcquire(arg)) { 
  9.              // 如果資源獲取成功,則將 head 指向自己 
  10.                 setHead(node); 
  11.                 p.next = null; // help GC 
  12.                 failed = false
  13.                 return interrupted; 
  14.             } 
  15.             // 節(jié)點(diǎn)進(jìn)入等待隊(duì)列后,調(diào)用 shouldParkAfterFailedAcquire 或者 parkAndCheckInterrupt 方法 
  16.             // 進(jìn)入阻塞狀態(tài),即只有頭結(jié)點(diǎn)的線程處于活躍狀態(tài) 
  17.             if (shouldParkAfterFailedAcquire(p, node) && 
  18.                 parkAndCheckInterrupt()) 
  19.                 interrupted = true
  20.         } 
  21.     } finally { 
  22.         if (failed) 
  23.             cancelAcquire(node); 
  24.     } 

在獲取資源時(shí),除了 acquire 之外,還有三個(gè)方法:

  • acquireInterruptibly :申請(qǐng)可中斷的資源(獨(dú)占模式)
  • acquireShared :申請(qǐng)共享模式的資源
  • acquireSharedInterruptibly :申請(qǐng)可中斷的資源(共享模式)

到這里,關(guān)于 AQS 如何獲取資源就說的差不多了,接下來看看 AQS 是如何釋放資源的

AQS 如何釋放資源

釋放資源相對(duì)于獲取資源來說,簡(jiǎn)單了很多。源碼如下:

  1. public final boolean release(int arg) { 
  2.  // 如果釋放鎖成功 
  3.     if (tryRelease(arg)) {  
  4.      // 獲取 AQS 隊(duì)列中的頭結(jié)點(diǎn) 
  5.         Node h = head; 
  6.         // 如果頭結(jié)點(diǎn)不為空,且狀態(tài) != 0 
  7.         if (h != null && h.waitStatus != 0) 
  8.          // 調(diào)用 unparkSuccessor(h) 方法,喚醒后續(xù)節(jié)點(diǎn) 
  9.             unparkSuccessor(h); 
  10.         return true
  11.     } 
  12.     return false
  13.  
  14. private void unparkSuccessor(Node node) { 
  15.     int ws = node.waitStatus; 
  16.     // 如果狀態(tài)是負(fù)數(shù),嘗試將它改為 0 
  17.     if (ws < 0) 
  18.         compareAndSetWaitStatus(node, ws, 0); 
  19.  // 得到頭結(jié)點(diǎn)的后繼節(jié)點(diǎn) 
  20.     Node s = node.next
  21.     // 如果 waitStatus 大于 0 ,說明這個(gè)節(jié)點(diǎn)被取消 
  22.     if (s == null || s.waitStatus > 0) { 
  23.         s = null
  24.         // 那就從尾節(jié)點(diǎn)開始,找到距離 head 最近的一個(gè) waitStatus<=0 的節(jié)點(diǎn)進(jìn)行喚醒 
  25.         for (Node t = tail; t != null && t != node; t = t.prev) 
  26.             if (t.waitStatus <= 0) 
  27.                 s = t; 
  28.     } 
  29.     // 如果后繼節(jié)點(diǎn)不為空,則將其從阻塞狀態(tài)變?yōu)榉亲枞麪顟B(tài) 
  30.     if (s != null
  31.         LockSupport.unpark(s.thread); 

AQS 兩種資源共享模式

資源有兩種共享模式:

  • 獨(dú)占模式( Exclusive ):資源是獨(dú)占的,也就是一次只能被一個(gè)線程占有,比如 ReentrantLock
  • 共享模式( Share ):同時(shí)可以被多個(gè)線程獲取,具體的資源個(gè)數(shù)可以通過參數(shù)來確定,比如 Semaphore/CountDownLatch

看到這里, AQS 你 get 了嘛?

 

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2019-11-19 10:32:55

Java語言程序員

2020-05-29 11:48:01

安全運(yùn)維信息安全網(wǎng)絡(luò)安全

2022-12-12 08:46:11

2020-05-22 08:13:45

敏捷開發(fā)OKR

2022-03-16 18:27:39

開發(fā)低代碼軟件開發(fā)

2022-08-26 01:10:32

TCPSYNLinux

2019-08-12 11:14:00

JVM垃圾對(duì)象

2019-07-29 10:10:06

Java內(nèi)存線程安全

2021-05-26 05:22:48

SQL 數(shù)據(jù)庫SELECT

2023-06-16 14:10:00

TCPUDP網(wǎng)絡(luò)通信

2021-03-12 09:24:58

Redis面試場(chǎng)景

2019-08-01 10:20:10

2022-05-30 18:37:03

數(shù)據(jù)個(gè)人信息人工智能

2019-10-09 10:00:02

集群故障場(chǎng)景

2019-10-09 09:39:15

PythonHDFS大數(shù)據(jù)

2014-11-26 14:46:47

代碼

2021-06-02 09:47:48

RSA2021

2023-02-13 13:37:20

ChatGPTAI微軟

2021-02-03 10:17:03

5G5G專網(wǎng)通信市場(chǎng)

2023-05-04 12:12:00

ChatGPTAI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩有码一区二区三区 | 狠狠干综合视频 | 很黄很污的网站 | 日韩视频在线观看一区二区 | 91久久国产综合久久 | 在线观看国产精品视频 | 亚洲一区视频在线 | 69av在线视频 | 日本在线观看视频 | 亚洲欧美视频一区二区 | 亚洲精品一区二区三区 | 色偷偷噜噜噜亚洲男人 | 中文字幕一区二区三区四区五区 | 欧美日韩一区二区三区四区 | 先锋影音资源网站 | 91精品国产综合久久久久久丝袜 | 免费一级大片 | 国产成人免费一区二区60岁 | 欧美一级特黄aaa大片在线观看 | 99久久免费精品 | 日韩精品成人免费观看视频 | 国产精品人人做人人爽 | 久久久精品日本 | 亚洲免费久久久 | 日本欧美黄色片 | 日韩精品免费在线 | 在线看片福利 | 一区二区三区四区电影视频在线观看 | 在线免费黄色 | 国产亚洲一区二区三区在线观看 | 国产清纯白嫩初高生在线播放视频 | 国产免费一区二区三区 | 久久久精品视频一区二区三区 | 久久久久久精 | 在线观看特色大片免费网站 | 三级高清 | 午夜在线 | 国产一区二区三区四区三区四 | 亚洲 欧美 激情 另类 校园 | 欧美日韩一区二区三区四区 | 激情五月婷婷 |