精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

我畫了35張圖就是為了讓你深入 AQS

開發 架構
談到并發,我們不得不說AQS(AbstractQueuedSynchronizer),所謂的AQS即是抽象的隊列式的同步器,內部定義了很多鎖相關的方法,我們熟知的ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore等都是基于AQS來實現的。

 [[324754]]

前言

談到并發,我們不得不說AQS(AbstractQueuedSynchronizer),所謂的AQS即是抽象的隊列式的同步器,內部定義了很多鎖相關的方法,我們熟知的ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore等都是基于AQS來實現的。

我們先看下AQS相關的UML圖:

 

1.AQS實現原理

AQS中 維護了一個volatile int state(代表共享資源)和一個FIFO線程等待隊列(多線程爭用資源被阻塞時會進入此隊列)。

這里volatile能夠保證多線程下的可見性,當state=1則代表當前對象鎖已經被占有,其他線程來加鎖時則會失敗,加鎖失敗的線程會被放入一個FIFO的等待隊列中,比列會被UNSAFE.park()操作掛起,等待其他獲取鎖的線程釋放鎖才能夠被喚醒。

另外state的操作都是通過CAS來保證其并發修改的安全性。

具體原理我們可以用一張圖來簡單概括:

 

AQS 中提供了很多關于鎖的實現方法,

  • getState():獲取鎖的標志state值
  • setState():設置鎖的標志state值
  • tryAcquire(int):獨占方式獲取鎖。嘗試獲取資源,成功則返回true,失敗則返回false。
  • tryRelease(int):獨占方式釋放鎖。嘗試釋放資源,成功則返回true,失敗則返回false。

這里還有一些方法并沒有列出來,接下來我們以ReentrantLock作為突破點通過源碼和畫圖的形式一步步了解AQS內部實現原理。

 

2.目錄結構

文章準備模擬多線程競爭鎖、釋放鎖的場景來進行分析AQS源碼:

三個線程(線程一、線程二、線程三)同時來加鎖/釋放鎖

目錄如下:

  • 線程一加鎖成功時AQS內部實現
  • 線程二/三加鎖失敗時AQS中等待隊列的數據模型
  • 線程一釋放鎖及線程二獲取鎖實現原理
  • 通過線程場景來講解公平鎖具體實現原理
  • 通過線程場景來講解Condition中await()和signal()實現原理

這里會通過畫圖來分析每個線程加鎖、釋放鎖后AQS內部的數據結構和實現原理

 

3.場景分析

線程一加鎖成功

如果同時有三個線程并發搶占鎖,此時線程一搶占鎖成功,線程二和線程三搶占鎖失敗,具體執行流程如下:

 

此時AQS內部數據為:

 

 

線程二、線程三加鎖失?。?/p>

 

有圖可以看出,等待隊列中的節點Node是一個雙向鏈表,這里SIGNAL是Node中waitStatus屬性,Node中還有一個nextWaiter屬性,這個并未在圖中畫出來,這個到后面Condition會具體講解的。

具體看下搶占鎖代碼實現:

java.util.concurrent.locks.ReentrantLock .NonfairSync:

  1. static final class NonfairSync extends Sync { 
  2.      
  3.     final void lock() { 
  4.         if (compareAndSetState(0, 1)) 
  5.             setExclusiveOwnerThread(Thread.currentThread()); 
  6.         else 
  7.             acquire(1); 
  8.     } 
  9.  
  10.     protected final boolean tryAcquire(int acquires) { 
  11.         return nonfairTryAcquire(acquires); 
  12.     } 

這里使用的ReentrantLock非公平鎖,線程進來直接利用CAS嘗試搶占鎖,如果搶占成功state值回被改為1,且設置對象獨占鎖線程為當前線程。如下所示:

  1. protected final boolean compareAndSetState(int expect, int update) { 
  2.     return unsafe.compareAndSwapInt(this, stateOffset, expect, update); 
  3.  
  4. protected final void setExclusiveOwnerThread(Thread thread) { 
  5.     exclusiveOwnerThread = thread; 

線程二搶占鎖失敗

我們按照真實場景來分析,線程一搶占鎖成功后,state變為1,線程二通過CAS修改state變量必然會失敗。此時AQS中FIFO(First In First Out 先進先出)隊列中數據如圖所示:

 

我們將線程二執行的邏輯一步步拆解來看:

java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire():

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

先看看tryAcquire()的具體實現:java.util.concurrent.locks.ReentrantLock .nonfairTryAcquire():

  1. final boolean nonfairTryAcquire(int acquires) { 
  2.     final Thread current = Thread.currentThread(); 
  3.     int c = getState(); 
  4.     if (c == 0) { 
  5.         if (compareAndSetState(0, acquires)) { 
  6.             setExclusiveOwnerThread(current); 
  7.             return true
  8.         } 
  9.     } 
  10.     else if (current == getExclusiveOwnerThread()) { 
  11.         int nextc = c + acquires; 
  12.         if (nextc < 0) 
  13.             throw new Error("Maximum lock count exceeded"); 
  14.         setState(nextc); 
  15.         return true
  16.     } 
  17.     return false

nonfairTryAcquire()方法中首先會獲取state的值,如果不為0則說明當前對象的鎖已經被其他線程所占有,接著判斷占有鎖的線程是否為當前線程,如果是則累加state值,這就是可重入鎖的具體實現,累加state值,釋放鎖的時候也要依次遞減state值。

如果state為0,則執行CAS操作,嘗試更新state值為1,如果更新成功則代表當前線程加鎖成功。

以線程二為例,因為線程一已經將state修改為1,所以線程二通過CAS修改state的值不會成功。加鎖失敗。

線程二執行tryAcquire()后會返回false,接著執行addWaiter(Node.EXCLUSIVE)邏輯,將自己加入到一個FIFO等待隊列中,代碼實現如下:

java.util.concurrent.locks.AbstractQueuedSynchronizer.addWaiter():

  1. private Node addWaiter(Node mode) {     
  2.     Node node = new Node(Thread.currentThread(), mode); 
  3.     Node pred = tail; 
  4.     if (pred != null) { 
  5.         node.prev = pred; 
  6.         if (compareAndSetTail(pred, node)) { 
  7.             pred.next = node; 
  8.             return node; 
  9.         } 
  10.     } 
  11.     enq(node); 
  12.     return node; 

這段代碼首先會創建一個和當前線程綁定的Node節點,Node為雙向鏈表。此時等待對內中的tail指針為空,直接調用enq(node)方法將當前線程加入等待隊列尾部:

  1. private Node enq(final Node node) { 
  2.     for (;;) { 
  3.         Node t = tail; 
  4.         if (t == null) { 
  5.             if (compareAndSetHead(new Node())) 
  6.                 tail = head; 
  7.         } else { 
  8.             node.prev = t; 
  9.             if (compareAndSetTail(t, node)) { 
  10.                 t.next = node; 
  11.                 return t; 
  12.             } 
  13.         } 
  14.     } 

第一遍循環時tail指針為空,進入if邏輯,使用CAS操作設置head指針,將head指向一個新創建的Node節點。此時AQS中數據:

 

執行完成之后,head、tail、t都指向第一個Node元素。

接著執行第二遍循環,進入else邏輯,此時已經有了head節點,這里要操作的就是將線程二對應的Node節點掛到head節點后面。此時隊列中就有了兩個Node節點:

 

addWaiter()方法執行完后,會返回當前線程創建的節點信息。繼續往后執行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)邏輯,此時傳入的參數為線程二對應的Node節點信息:

java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued():

  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.             if (p == head && tryAcquire(arg)) { 
  8.                 setHead(node); 
  9.                 p.next = null; // help GC 
  10.                 failed = false
  11.                 return interrupted; 
  12.             } 
  13.             if (shouldParkAfterFailedAcquire(p, node) && 
  14.                 parkAndChecknIterrupt()) 
  15.                 interrupted = true
  16.         } 
  17.     } finally { 
  18.         if (failed) 
  19.             cancelAcquire(node); 
  20.     } 
  21.  
  22. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 
  23.     int ws = pred.waitStatus; 
  24.     if (ws == Node.SIGNAL) 
  25.         return true
  26.     if (ws > 0) { 
  27.         do { 
  28.             node.prev = pred = pred.prev; 
  29.         } while (pred.waitStatus > 0); 
  30.         pred.next = node; 
  31.     } else { 
  32.         compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 
  33.     } 
  34.     return false
  35.  
  36. private final boolean parkAndCheckInterrupt() { 
  37.     LockSupport.park(this); 
  38.     return Thread.interrupted(); 

acquireQueued()這個方法會先判斷當前傳入的Node對應的前置節點是否為head,如果是則嘗試加鎖。加鎖成功過則將當前節點設置為head節點,然后空置之前的head節點,方便后續被垃圾回收掉。

如果加鎖失敗或者Node的前置節點不是head節點,就會通過shouldParkAfterFailedAcquire方法 將head節點的waitStatus變為了SIGNAL=-1,最后執行parkAndChecknIterrupt方法,調用LockSupport.park()掛起當前線程。

此時AQS中的數據如下圖:

 

此時線程二就靜靜的待在AQS的等待隊列里面了,等著其他線程釋放鎖來喚醒它。

線程三搶占鎖失敗

看完了線程二搶占鎖失敗的分析,那么再來分析線程三搶占鎖失敗就很簡單了,先看看addWaiter(Node mode)方法:

  1. private Node addWaiter(Node mode) { 
  2.     Node node = new Node(Thread.currentThread(), mode); 
  3.     Node pred = tail; 
  4.     if (pred != null) { 
  5.         node.prev = pred; 
  6.         if (compareAndSetTail(pred, node)) { 
  7.             pred.next = node; 
  8.             return node; 
  9.         } 
  10.     } 
  11.     enq(node); 
  12.     return node; 

此時等待隊列的tail節點指向線程二,進入if邏輯后,通過CAS指令將tail節點重新指向線程三。接著線程三調用enq()方法執行入隊操作,和上面線程二執行方式是一致的,入隊后會修改線程二對應的Node中的waitStatus=SIGNAL。最后線程三也會被掛起。此時等待隊列的數據如圖:

 

線程一釋放鎖

現在來分析下釋放鎖的過程,首先是線程一釋放鎖,釋放鎖后會喚醒head節點的后置節點,也就是我們現在的線程二,具體操作流程如下:

 

執行完后等待隊列數據如下:

 

此時線程二已經被喚醒,繼續嘗試獲取鎖,如果獲取鎖失敗,則會繼續被掛起。如果獲取鎖成功,則AQS中數據如圖:

 

接著還是一步步拆解來看,先看看線程一釋放鎖的代碼:

java.util.concurrent.locks.AbstractQueuedSynchronizer.release()

  1. public final boolean release(int arg) { 
  2.     if (tryRelease(arg)) { 
  3.         Node h = head; 
  4.         if (h != null && h.waitStatus != 0) 
  5.             unparkSuccessor(h); 
  6.         return true
  7.     } 
  8.     return false

這里首先會執行tryRelease()方法,這個方法具體實現在ReentrantLock中,如果tryRelease執行成功,則繼續判斷head節點的waitStatus是否為0,前面我們已經看到過,head的waitStatue為SIGNAL(-1),這里就會執行unparkSuccessor()方法來喚醒head的后置節點,也就是我們上面圖中線程二對應的Node節點。

此時看ReentrantLock.tryRelease()中的具體實現:

  1. protected final boolean tryRelease(int releases) { 
  2.     int c = getState() - releases; 
  3.     if (Thread.currentThread() != getExclusiveOwnerThread()) 
  4.         throw new IllegalMonitorStateException(); 
  5.     boolean free = false
  6.     if (c == 0) { 
  7.         free = true
  8.         setExclusiveOwnerThread(null); 
  9.     } 
  10.     setState(c); 
  11.     return free

執行完ReentrantLock.tryRelease()后,state被設置成0,Lock對象的獨占鎖被設置為null。此時看下AQS中的數據:

 

接著執行java.util.concurrent.locks.AbstractQueuedSynchronizer.unparkSuccessor()方法,喚醒head的后置節點:

  1. private void unparkSuccessor(Node node) { 
  2.     int ws = node.waitStatus; 
  3.     if (ws < 0) 
  4.         compareAndSetWaitStatus(node, ws, 0); 
  5.     Node s = node.next
  6.     if (s == null || s.waitStatus > 0) { 
  7.         s = null
  8.         for (Node t = tail; t != null && t != node; t = t.prev) 
  9.             if (t.waitStatus <= 0) 
  10.                 s = t; 
  11.     } 
  12.     if (s != null
  13.         LockSupport.unpark(s.thread); 

這里主要是將head節點的waitStatus設置為0,然后解除head節點next的指向,使head節點空置,等待著被垃圾回收。

此時重新將head指針指向線程二對應的Node節點,且使用LockSupport.unpark方法來喚醒線程二。

被喚醒的線程二會接著嘗試獲取鎖,用CAS指令修改state數據。執行完成后可以查看AQS中數據:

 

此時線程二被喚醒,線程二接著之前被park的地方繼續執行,繼續執行acquireQueued()方法。

線程二喚醒繼續加鎖

  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.             if (p == head && tryAcquire(arg)) { 
  8.                 setHead(node); 
  9.                 p.next = null; // help GC 
  10.                 failed = false
  11.                 return interrupted; 
  12.             } 
  13.             if (shouldParkAfterFailedAcquire(p, node) && 
  14.                 parkAndCheckInterrupt()) 
  15.                 interrupted = true
  16.         } 
  17.     } finally { 
  18.         if (failed) 
  19.             cancelAcquire(node); 
  20.     } 

此時線程二被喚醒,繼續執行for循環,判斷線程二的前置節點是否為head,如果是則繼續使用tryAcquire()方法來嘗試獲取鎖,其實就是使用CAS操作來修改state值,如果修改成功則代表獲取鎖成功。接著將線程二設置為head節點,然后空置之前的head節點數據,被空置的節點數據等著被垃圾回收。

此時線程三獲取鎖成功,AQS中隊列數據如下:

 

等待隊列中的數據都等待著被垃圾回收。

線程二釋放鎖/線程三加鎖

當線程二釋放鎖時,會喚醒被掛起的線程三,流程和上面大致相同,被喚醒的線程三會再次嘗試加鎖,具體代碼可以參考上面內容。具體流程圖如下:

 

此時AQS中隊列數據如圖:

 

4公平鎖實現原理

上面所有的加鎖場景都是基于非公平鎖來實現的,非公平鎖是ReentrantLock的默認實現,那我們接著來看一下公平鎖的實現原理,這里先用一張圖來解釋公平鎖和非公平鎖的區別:

非公平鎖執行流程:

 

 

 

 

這里我們還是用之前的線程模型來舉例子,當線程二釋放鎖的時候,喚醒被掛起的線程三,線程三執行tryAcquire()方法使用CAS操作來嘗試修改state值,如果此時又來了一個線程四也來執行加鎖操作,同樣會執行tryAcquire()方法。

這種情況就會出現競爭,線程四如果獲取鎖成功,線程三仍然需要待在等待隊列中被掛起。這就是所謂的非公平鎖,線程三辛辛苦苦排隊等到自己獲取鎖,卻眼巴巴的看到線程四插隊獲取到了鎖。

公平鎖執行流程:

 

公平鎖在加鎖的時候,會先判斷AQS等待隊列中是存在節點,如果存在節點則會直接入隊等待,具體代碼如下.

公平鎖在獲取鎖是也是首先會執行acquire()方法,只不過公平鎖單獨實現了tryAcquire()方法:

#java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire():

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

這里會執行ReentrantLock中公平鎖的tryAcquire()方法

#java.util.concurrent.locks.ReentrantLock.FairSync.tryAcquire():

  1. static final class FairSync extends Sync { 
  2.     protected final boolean tryAcquire(int acquires) { 
  3.         final Thread current = Thread.currentThread(); 
  4.         int c = getState(); 
  5.         if (c == 0) { 
  6.             if (!hasQueuedPredecessors() && 
  7.                 compareAndSetState(0, acquires)) { 
  8.                 setExclusiveOwnerThread(current); 
  9.                 return true
  10.             } 
  11.         } 
  12.         else if (current == getExclusiveOwnerThread()) { 
  13.             int nextc = c + acquires; 
  14.             if (nextc < 0) 
  15.                 throw new Error("Maximum lock count exceeded"); 
  16.             setState(nextc); 
  17.             return true
  18.         } 
  19.         return false
  20.     } 

這里會先判斷state值,如果不為0且獲取鎖的線程不是當前線程,直接返回false代表獲取鎖失敗,被加入等待隊列。如果是當前線程則可重入獲取鎖。

如果state=0則代表此時沒有線程持有鎖,執行hasQueuedPredecessors()判斷AQS等待隊列中是否有元素存在,如果存在其他等待線程,那么自己也會加入到等待隊列尾部,做到真正的先來后到,有序加鎖。具體代碼如下:

#java.util.concurrent.locks.AbstractQueuedSynchronizer.hasQueuedPredecessors():

  1. public final boolean hasQueuedPredecessors() { 
  2.     Node t = tail; 
  3.     Node h = head; 
  4.     Node s; 
  5.     return h != t && 
  6.         ((s = h.next) == null || s.thread != Thread.currentThread()); 

這段代碼很有意思,返回false代表隊列中沒有節點或者僅有一個節點是當前線程創建的節點。返回true則代表隊列中存在等待節點,當前線程需要入隊等待。

 

先判斷head是否等于tail,如果隊列中只有一個Node節點,那么head會等于tail,接著判斷head的后置節點,這里肯定會是null,如果此Node節點對應的線程和當前的線程是同一個線程,那么則會返回false,代表沒有等待節點或者等待節點就是當前線程創建的Node節點。此時當前線程會嘗試獲取鎖。

如果head和tail不相等,說明隊列中有等待線程創建的節點,此時直接返回true,如果只有一個節點,而此節點的線程和當前線程不一致,也會返回true

非公平鎖和公平鎖的區別:非公平鎖性能高于公平鎖性能。非公平鎖可以減少CPU喚醒線程的開銷,整體的吞吐效率會高點,CPU也不必取喚醒所有線程,會減少喚起線程的數量

非公平鎖性能雖然優于公平鎖,但是會存在導致線程饑餓的情況。在最壞的情況下,可能存在某個線程一直獲取不到鎖。不過相比性能而言,饑餓問題可以暫時忽略,這可能就是ReentrantLock默認創建非公平鎖的原因之一了。

 

5.Condition實現原理

Condition 簡介

上面已經介紹了AQS所提供的核心功能,當然它還有很多其他的特性,這里我們來繼續說下Condition這個組件。

Condition是在java 1.5中才出現的,它用來替代傳統的Object的wait()、notify()實現線程間的協作,相比使用Object的wait()、notify(),使用Condition中的await()、signal()這種方式實現線程間協作更加安全和高效。因此通常來說比較推薦使用Condition

其中AbstractQueueSynchronizer中實現了Condition中的方法,主要對外提供awaite(Object.wait())和signal(Object.notify())調用。

Condition Demo示例

使用示例代碼:

  1. /** 
  2.  * ReentrantLock 實現源碼學習 
  3.  * @author 一枝花算不算浪漫 
  4.  * @date 2020/4/28 7:20 
  5.  */ 
  6. public class ReentrantLockDemo { 
  7.     static ReentrantLock lock = new ReentrantLock(); 
  8.  
  9.     public static void main(String[] args) { 
  10.         Condition condition = lock.newCondition(); 
  11.  
  12.         new Thread(() -> { 
  13.             lock.lock(); 
  14.             try { 
  15.                 System.out.println("線程一加鎖成功"); 
  16.                 System.out.println("線程一執行await被掛起"); 
  17.                 condition.await(); 
  18.                 System.out.println("線程一被喚醒成功"); 
  19.             } catch (Exception e) { 
  20.                 e.printStackTrace(); 
  21.             } finally { 
  22.                 lock.unlock(); 
  23.                 System.out.println("線程一釋放鎖成功"); 
  24.             } 
  25.         }).start(); 
  26.  
  27.         new Thread(() -> { 
  28.             lock.lock(); 
  29.             try { 
  30.                 System.out.println("線程二加鎖成功"); 
  31.                 condition.signal(); 
  32.                 System.out.println("線程二喚醒線程一"); 
  33.             } finally { 
  34.                 lock.unlock(); 
  35.                 System.out.println("線程二釋放鎖成功"); 
  36.             } 
  37.         }).start(); 
  38.     } 

執行結果如下圖:

 

這里線程一先獲取鎖,然后使用await()方法掛起當前線程并釋放鎖,線程二獲取鎖后使用signal喚醒線程一。

Condition實現原理圖解

我們還是用上面的demo作為實例,執行的流程如下:

 

線程一執行await()方法:

先看下具體的代碼實現,#java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.await():

  1. public final void await() throws InterruptedException { 
  2.     if (Thread.interrupted()) 
  3.         throw new InterruptedException(); 
  4.     Node node = addConditionWaiter(); 
  5.     int savedState = fullyRelease(node); 
  6.     int interruptMode = 0; 
  7.     while (!isOnSyncQueue(node)) { 
  8.         LockSupport.park(this); 
  9.         if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 
  10.             break; 
  11.     } 
  12.     if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 
  13.         interruptMode = REINTERRUPT; 
  14.     if (node.nextWaiter != null) // clean up if cancelled 
  15.         unlinkCancelledWaiters(); 
  16.     if (interruptMode != 0) 
  17.         reportInterruptAfterWait(interruptMode); 

await()方法中首先調用addConditionWaiter()將當前線程加入到Condition隊列中。

執行完后我們可以看下Condition隊列中的數據:

 

具體實現代碼為:

  1. private Node addConditionWaiter() { 
  2.     Node t = lastWaiter; 
  3.     if (t != null && t.waitStatus != Node.CONDITION) { 
  4.         unlinkCancelledWaiters(); 
  5.         t = lastWaiter; 
  6.     } 
  7.     Node node = new Node(Thread.currentThread(), Node.CONDITION); 
  8.     if (t == null
  9.         firstWaiter = node; 
  10.     else 
  11.         t.nextWaiter = node; 
  12.     lastWaiter = node; 
  13.     return node; 

這里會用當前線程創建一個Node節點,waitStatus為CONDITION。接著會釋放該節點的鎖,調用之前解析過的release()方法,釋放鎖后此時會喚醒被掛起的線程二,線程二會繼續嘗試獲取鎖。

接著調用isOnSyncQueue()方法判斷當前節點是否為Condition隊列中的頭部節點,如果是則調用LockSupport.park(this)掛起Condition中當前線程。此時線程一被掛起,線程二獲取鎖成功。

具體流程如下圖:

 

線程二執行signal()方法:

首先我們考慮下線程二已經獲取到鎖,此時AQS等待隊列中已經沒有了數據。

接著就來看看線程二喚醒線程一的具體執行流程:

  1. public final void signal() { 
  2.     if (!isHeldExclusively()) 
  3.         throw new IllegalMonitorStateException(); 
  4.     Node first = firstWaiter; 
  5.     if (first != null
  6.         doSignal(first); 

先判斷當前線程是否為獲取鎖的線程,如果不是則直接拋出異常。接著調用doSignal()方法來喚醒線程。

  1. private void doSignal(Node first) { 
  2.     do { 
  3.         if ( (firstWaiter = first.nextWaiter) == null
  4.             lastWaiter = null
  5.         first.nextWaiter = null
  6.     } while (!transferForSignal(first) && 
  7.              (first = firstWaiter) != null); 
  8.  
  9. final boolean transferForSignal(Node node) { 
  10.     if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) 
  11.         return false
  12.  
  13.     Node p = enq(node); 
  14.     int ws = p.waitStatus; 
  15.     if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) 
  16.         LockSupport.unpark(node.thread); 
  17.     return true
  18.  
  19. /** 
  20.  * Inserts node into queue, initializing if necessary. See picture above. 
  21.  * @param node the node to insert 
  22.  * @return node's predecessor 
  23.  */ 
  24. private Node enq(final Node node) { 
  25.     for (;;) { 
  26.         Node t = tail; 
  27.         if (t == null) { // Must initialize 
  28.             if (compareAndSetHead(new Node())) 
  29.                 tail = head; 
  30.         } else { 
  31.             node.prev = t; 
  32.             if (compareAndSetTail(t, node)) { 
  33.                 t.next = node; 
  34.                 return t; 
  35.             } 
  36.         } 
  37.     } 

這里先從transferForSignal()方法來看,通過上面的分析我們知道Condition隊列中只有線程一創建的一個Node節點,且waitStatue為CONDITION,先通過CAS修改當前節點waitStatus為0,然后執行enq()方法將當前線程加入到等待隊列中,并返回當前線程的前置節點。

加入等待隊列的代碼在上面也已經分析過,此時等待隊列中數據如下圖:

 

接著開始通過CAS修改當前節點的前置節點waitStatus為SIGNAL,并且喚醒當前線程。此時AQS中等待隊列數據為:

 

線程一被喚醒后,繼續執行await()方法中的 while 循環。

  1. public final void await() throws InterruptedException { 
  2.     if (Thread.interrupted()) 
  3.         throw new InterruptedException(); 
  4.     Node node = addConditionWaiter(); 
  5.     int savedState = fullyRelease(node); 
  6.     int interruptMode = 0; 
  7.     while (!isOnSyncQueue(node)) { 
  8.         LockSupport.park(this); 
  9.         if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 
  10.             break; 
  11.     } 
  12.     if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 
  13.         interruptMode = REINTERRUPT; 
  14.     if (node.nextWaiter != null) // clean up if cancelled 
  15.         unlinkCancelledWaiters(); 
  16.     if (interruptMode != 0) 
  17.         reportInterruptAfterWait(interruptMode); 

因為此時線程一的waitStatus已經被修改為0,所以執行isOnSyncQueue()方法會返回false。跳出while循環。

接著執行acquireQueued()方法,這里之前也有講過,嘗試重新獲取鎖,如果獲取鎖失敗繼續會被掛起。直到另外線程釋放鎖才被喚醒。

  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.             if (p == head && tryAcquire(arg)) { 
  8.                 setHead(node); 
  9.                 p.next = null; // help GC 
  10.                 failed = false
  11.                 return interrupted; 
  12.             } 
  13.             if (shouldParkAfterFailedAcquire(p, node) && 
  14.                 parkAndCheckInterrupt()) 
  15.                 interrupted = true
  16.         } 
  17.     } finally { 
  18.         if (failed) 
  19.             cancelAcquire(node); 
  20.     } 

此時線程一的流程都已經分析完了,等線程二釋放鎖后,線程一會繼續重試獲取鎖,流程到此終結。

Condition總結

我們總結下 Condition 和 wait/notify 的比較:

  • Condition 可以精準的對多個不同條件進行控制,wait/notify 只能和 synchronized 關鍵字一起使用,并且只能喚醒一個或者全部的等待隊列;
  • Condition 需要使用 Lock 進行控制,使用的時候要注意 lock() 后及時的 unlock(),Condition 有類似于 await 的機制,因此不會產生加鎖方式而產生的死鎖出現,同時底層實現的是 park/unpark 的機制,因此也不會產生先喚醒再掛起的死鎖,一句話就是不會產生死鎖,但是 wait/notify 會產生先喚醒再掛起的死鎖。

 

6.總結

這里用了一步一圖的方式結合三個線程依次加鎖/釋放鎖來展示了ReentrantLock的實現方式和實現原理,而ReentrantLock底層就是基于AQS實現的,所以我們也對AQS有了深刻的理解。

另外還介紹了公平鎖與非公平鎖的實現原理,Condition的實現原理,基本上都是使用源碼+繪圖的講解方式,盡量讓大家更容易去理解。

責任編輯:武曉燕 來源: 程序員cxuan
相關推薦

2022-12-26 08:36:24

JavaMESA模型

2020-01-02 09:14:23

Kubernetes內部容器

2021-12-02 15:20:49

Redis數據結構

2021-03-23 10:25:05

Redis數據結構

2009-11-11 09:39:01

張汝京辭職

2012-01-05 09:26:56

App Store作產品賺錢

2016-12-16 12:06:09

數據分析大數據

2013-07-09 14:22:56

Windows 8.1

2020-06-04 07:55:33

ReentrantLo Java

2016-10-19 12:54:15

數據聚類關聯

2011-12-16 16:37:02

Fabrics邊界軟件數據中心

2021-12-17 17:52:02

MySQL B+面試

2020-11-03 10:32:48

回調函數模塊

2017-05-22 14:45:51

大數據神經網絡架構

2013-03-08 09:54:25

2023-09-28 21:37:41

HashMap多線程

2014-03-14 09:47:08

手游進化產品

2021-08-11 22:17:48

負載均衡LVS機制

2020-04-08 14:21:15

Google 黑科技達芬奇

2021-05-18 06:55:07

Java AQS源碼
點贊
收藏

51CTO技術棧公眾號

日韩精品一二三| 成人av激情人伦小说| 亚洲欧洲日产国产综合网| 成人免费看片视频| 久久久久久久久97| 欧美电影完整版在线观看| 欧美日韩在线看| 亚洲精品自在在线观看| 国产高清第一页| 国产精品主播| 精品国内亚洲在观看18黄| 9191在线视频| 欧美aaa大片视频一二区| 亚洲精品国产第一综合99久久| 91久久久久久久久久久| 国产精品美女毛片真酒店| 精品国产一区二区三区香蕉沈先生 | 少妇高潮在线观看| 日韩中文字幕一区二区高清99| 一二三区精品视频| 色女人综合av| 天天av天天翘| 国产精品一区在线观看你懂的| 欧美精品video| 午夜黄色福利视频| 国产精品探花在线观看| 亚洲成年人影院在线| 亚洲一区日韩精品| 在线女人免费视频| 亚洲精选视频在线| 免费久久一级欧美特大黄| 99在线观看精品视频| 免费国产自线拍一欧美视频| 久久久国产视频91| www.久久国产| 7m精品国产导航在线| 欧洲亚洲精品在线| av在线播放亚洲| 日本在线视频网| 91久色porny| 99在线首页视频| 中文字幕 人妻熟女| 国产伊人精品| 久久精品99久久久久久久久| 91精品人妻一区二区| 老司机亚洲精品一区二区| 一本久久a久久精品亚洲| 久久久久久久久网| wwwww在线观看免费视频| 成人av网站在线观看| 91久久久久久久久久久久久| 亚洲国产av一区二区三区| 韩日精品视频| 久久综合电影一区| 大乳护士喂奶hd| 欧美一级二级视频| 黑人狂躁日本妞一区二区三区| 综合视频免费看| 伦理片一区二区三区| aaa欧美日韩| 成人久久18免费网站漫画| 又骚又黄的视频| 久久午夜影视| 日韩av免费网站| 久久国产视频播放| 夜夜嗨一区二区三区| 色综合天天狠天天透天天伊人 | 成人a免费在线看| 91久久久久久| ,亚洲人成毛片在线播放| 日本亚洲三级在线| 国产精品久久久久av免费| 亚洲影院在线播放| 香蕉久久夜色精品| 2019中文字幕全在线观看| 国产精品变态另类虐交| 国产在线不卡| 午夜欧美大片免费观看| 日本一级黄色录像| 999在线观看精品免费不卡网站| 久久久极品av| 无码人妻精品一区二区三区夜夜嗨| 精品一区二区三区在线| 一区二区欧美日韩视频| 久久久久无码精品国产sm果冻| 青青视频一区二区| 日韩成人网免费视频| 欧美亚一区二区三区| 亚洲精品播放| 亚洲人在线视频| 美国美女黄色片| 五月天综合网站| 中文字幕v亚洲ⅴv天堂| 99热6这里只有精品| 在线一区电影| 国内精品久久久久影院 日本资源| 免费一级片视频| 极品裸体白嫩激情啪啪国产精品| 欧美黄色三级网站| 天堂网av手机版| 久久综合中文| 成人两性免费视频| 亚洲精品.www| 久久久精品人体av艺术| 亚洲国产欧洲综合997久久 | 亚洲毛片在线看| 亚洲精品成人av久久| 99久久精品国产亚洲精品| 久久成人在线视频| 国产成人免费观看视频 | 国产麻豆天美果冻无码视频| 欧美极品在线观看| 日韩在线视频播放| 青青草精品在线视频| 一本色道88久久加勒比精品| 日本精品视频在线| 国产视频在线观看视频| 99久久99久久综合| 一区二区三区四区欧美| 91九色国产在线播放| 欧美色窝79yyyycom| 精品无码av一区二区三区| 久久av网址| 欧美成人激情视频| 久久国产视频一区| 韩日av一区二区| 久久久com| 黄网站视频在线观看| 欧美性极品xxxx做受| 欧美午夜精品理论片| 亚洲电影一级片| 久久综合国产精品台湾中文娱乐网| 国产精品1234区| 美腿丝袜在线亚洲一区| 国产一区高清视频| 国产精品剧情| 欧美视频一区二区三区四区| 国产人妖在线观看| 91tv官网精品成人亚洲| 国产99视频在线观看| 亚洲精品久久久蜜桃动漫| 国产日产欧美一区二区视频| 国产真人做爰毛片视频直播| 五月天色综合| 国产性色av一区二区| 日本一区二区免费在线观看| 国产在线播精品第三| 色婷婷精品国产一区二区三区| 免费av不卡在线观看| 欧美精品久久一区二区三区| 亚洲综合色一区| 欧美日韩精选| 91久久久久久久久久| 中文字幕日本在线观看| 在线欧美日韩精品| 添女人荫蒂视频| 精品电影一区| 97超级碰碰| 国产不卡在线| 在线播放亚洲一区| 蜜桃av免费在线观看| 狂野欧美性猛交xxxx巴西| 久久精品99久久| 女厕盗摄一区二区三区| 日韩欧美激情一区| 97在线观看视频免费| 日日欢夜夜爽一区| 欧美日韩另类丝袜其他| 色在线视频观看| 亚洲精品狠狠操| 在线观看国产亚洲| av午夜一区麻豆| 熟女少妇在线视频播放| 亚洲第一二三区| 日韩免费在线视频| 久青草国产在线| 在线一区二区视频| 天堂在线中文视频| 青青草国产成人99久久| 亚洲v欧美v另类v综合v日韩v| 澳门成人av网| 亚洲欧美日韩网| 中文字幕日韩免费| 中文字幕高清一区| 天天干天天色天天干| 久久精品国产68国产精品亚洲| 欧美亚洲另类视频| 亚洲 小说区 图片区 都市| 一本大道久久a久久精二百| 久久久久亚洲av成人无码电影| 美女视频一区免费观看| 欧美日韩一区二区三区在线视频 | 日韩熟女精品一区二区三区| 99久久精品国产麻豆演员表| 国产a级一级片| 成人同人动漫免费观看| 成人网址在线观看| 三级福利片在线观看| 精品久久久久99| 日韩精品国产一区二区| 久久久久88色偷偷免费| 中文字幕线观看| 欧美三级第一页| 久久影院理伦片| 日韩精品一级毛片在线播放| 美日韩丰满少妇在线观看| 国产成人a人亚洲精品无码| 一区二区三区在线免费视频| 久久国产精品无码一级毛片| 玖玖精品视频| 国产又粗又猛又爽又黄的网站 | 国产偷国产偷亚洲高清97cao| 亚洲奶水xxxx哺乳期| 日韩黄色高清视频| 亚洲一区二区三区高清视频| 一区二区三区不卡视频在线观看| 国产老熟女伦老熟妇露脸| 日本中文字幕一区二区视频| 特级西西444| 国产成人精品一区二区免费看京| 成人欧美一区二区三区黑人| 国产福利电影在线播放| 色吧影院999| www.成人在线观看| 五月激情丁香一区二区三区| 男人的午夜天堂| jlzzjlzz亚洲日本少妇| 欧美精品一区免费| 一区在线视频观看| 亚洲一卡二卡三卡| 日韩精品欧美大片| 99国产在线观看| 成人在线中文| 欧美在线视频观看| av在线播放国产| 国产一区二区三区三区在线观看| 精品国自产在线观看| 一本久久综合亚洲鲁鲁五月天| 999精品视频在线观看播放| 91亚洲精品久久久蜜桃网站 | 电影k8一区二区三区久久| 在线看日韩欧美| 手机在线不卡av| 欧美一二三区精品| 中文字幕在线观看精品| 调教+趴+乳夹+国产+精品| 欧美黄片一区二区三区| 日本一区二区免费在线| 亚洲精品成人无码熟妇在线| 国产福利一区在线观看| 日韩欧美理论片| 老鸭窝91久久精品色噜噜导演| 久久久久久久久久久久久国产| 亚洲制服欧美另类| 91精品久久久久久久久久久久久久| 在线天堂中文资源最新版| 久久亚洲成人精品| 欧美a在线看| 在线免费观看羞羞视频一区二区| 亚洲日本香蕉视频| 亚洲国产毛片完整版| 亚洲精品综合久久| 日韩视频国产视频| a毛片在线免费观看| 91精品久久久久久蜜臀| 国产老女人乱淫免费| 欧美福利视频一区| 国产欧美一区二区三区视频在线观看| 欧美午夜片欧美片在线观看| 日韩无码精品一区二区三区| 午夜精品一区二区三区三上悠亚| 国产又粗又硬又长又爽| 国产精品短视频| 国产美女久久久久久| 亚洲视频中文字幕| 免费视频一二三区| 亚洲成人综合视频| 午夜毛片在线观看| 91精品福利在线| 中文天堂在线播放| 91精品国产综合久久精品麻豆| 97人妻精品一区二区三区软件| 欧美日精品一区视频| 国产一区二区三区在线观看| 日韩欧美国产午夜精品| 99久久久久久久| 日韩福利在线播放| 国产女人在线视频| 欧美xxxx做受欧美.88| 欧美14一18处毛片| 91国产视频在线播放| 欧美成人h版| 91在线免费看网站| 牛牛影视一区二区三区免费看| 麻豆精品传媒视频| 成人羞羞视频在线看网址| 青青草免费在线视频观看| 在线国产精品一区| 精品久久久久久久久久中文字幕| 一区视频在线看| 欧美精品成人网| 国产一区二区网址| 伊人网综合视频| 中文字幕免费观看一区| 青青草原国产视频| 日韩欧美国产一区二区| 国产素人在线观看| 激情亚洲成人| 日韩免费在线观看av| 亚洲女同同性videoxma| 色乱码一区二区三区在线| 国产成人三级在线观看| 在线免费观看黄色小视频| 中文字幕av不卡| 欧美日韩精品在线观看视频| 精品国产老师黑色丝袜高跟鞋| 波多野结衣一区二区三区四区| 欧美日韩精品免费| 日本wwwxxxx| 亚洲图中文字幕| 欧美另类tv| 国产精国产精品| 日韩影片在线观看| 欧美一区二区视频在线| 中文字幕亚洲精品乱码| 男人靠女人免费视频网站| 精品夜夜嗨av一区二区三区| 国产麻豆xxxvideo实拍| 综合网在线视频| 日韩精品在线免费视频| 日韩三级在线观看| 国产免费永久在线观看| 久久这里有精品视频| 亚洲男人av| 精品国产乱码一区二区三区四区| 成人网18免费网站| 69堂免费视频| 国产成人av一区二区三区在线观看| 久久久精品人妻无码专区| 亚洲综合一二区| 91丨porny丨在线中文 | 精品人妻无码一区二区性色| 91麻豆精品国产91久久久| 激情福利在线| 欧美成人免费全部| 欧美大胆a人体大胆做受| 国产伦精品一区二区三区视频黑人 | 亚洲深深色噜噜狠狠爱网站| 国产高清www| 国产精品一区二区在线播放| 久久免费手机视频| 一本色道久久综合亚洲91| 五月婷婷六月丁香| 欧美日本国产在线| 国产精品白丝久久av网站| 亚洲精品一区二区三| 日韩国产精品久久久久久亚洲| 中文字幕永久免费| 亚洲美女在线国产| 91在线你懂的| 一区二区三区日韩在线| 成人爱爱网址| 精品国产一区二区三区四区精华| 欧美高清不卡| 日本少妇xxx| 亚洲黄色av一区| 国产黄色一区二区| 欧美床上激情在线观看| 国产精品国产三级在线观看| 一区二区三区四区| 久久精品二区亚洲w码| 18啪啪污污免费网站| 精品视频在线免费看| 自拍视频在线| 91久久中文字幕| 欧美a级片网站| 99riav国产精品视频| 亚洲一区二区三区激情| a视频免费在线观看| 美女啪啪无遮挡免费久久网站| 精品女同一区二区三区在线观看| 先锋在线资源一区二区三区| 免费在线一区观看| 激情五月深爱五月| 777久久久精品| 午夜伦理大片视频在线观看| 国产精品精品软件视频| 国产精品久久久久久久久久妞妞| 亚洲黄色免费在线观看| 在线影视一区二区三区| 米奇精品一区二区三区| 国产精品夜色7777狼人| 欧美三级第一页| 免费在线观看成年人视频| 91国内精品野花午夜精品| 日韩精品成人av| 99九九视频| 天堂成人国产精品一区| 国产乱子轮xxx农村| 日韩免费看网站|