㈠ AQS源碼解析(6)acquireInterruptibly
reentrantlock中有lockInterruptibly函數,表示可響應中斷,之前講的lock是不會響應中斷的:
lockInterruptibly核心函數:
doAcquireInterruptibly:
㈡ 多線程-AQS總結
一個排他鎖ReentrantLock,一個ReenTrantReadWriteLock,3個同步器分別是CountDownLatch、Semaphore、CyclicBarrier(內部直接使用ReentrantLock,其他都有一個內部類Sync extends AQS)
如上圖Node節點,下面解析一下屬性
其中waitStatus是個枚舉
源碼閱讀見另外一篇博客
例如現在有3個線程A、B、C按順序獲取鎖,並且A線程一直佔用鎖
源碼閱讀見另外一篇博客
例如現在有6個線程A(讀)、B(讀)、C(寫)、D(讀)、E(讀)、F(寫)按順序獲取鎖
源碼閱讀見另外一篇博客
例如,主線程A創建CountDownLatch(2),創建2條線程的線程池,線程B、C
例如,主線程A創建CyclicBarrier(2,屏障點方法),創建2條線程的線程池,線程B、C
例如,線程A創建Semaphore(2),創建3條線程的線程池,線程B、C、D
參考地址: https://blog.csdn.net/wangnanwlw/article/details/109509685?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.not_use_machine_learn_pai&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.not_use_machine_learn_pai
㈢ AQS研究系列(一)--Unsafe使用
為了研究AQS,我們先來學習下java中cas(Compare And Swap)的基礎Unsafe類的使用
Unsafe產生於java無法向c那樣操作底層操作系統,但一些場景又需要相關操作.所以此類提供了一些java語言對於操作系統內存層面操作的API.這顯然被認為是不安全的,所以此類是不公開的,不建議被java應用直接使用.
但現實中已經有大量的java並發相關操作的框架在使用它了....據說此類在計劃廢棄中.
Unsafe能操作內存?這個是什麼概念?都有哪些操作呢?
其實最明顯的是它大量方法都是直接操作內存地址進行操作的.方法可以分為下面幾類:
我們可以使用LockSupport類進行操作
a. LockSupport.park()對應Unsafe的Unsafe.park(false, 0L)------>給當前所在線程加鎖,第一個參數表示true為精度型單位為納秒,false單位毫秒,第二次參數表示等待時間;
b. LockSupport.park.unpark --------->Thread thread對應Unsafe的UNSAFE.unpark(thread)方法(解鎖指定線程)
如果,我們直接使用Unsafe,是這樣子的:
我們還可以通過Unsafe類獲取對象的屬性值.因為Unsafe類是直接操作內存的,所以需要我們獲得對應的屬性內存地址,如下操作:
如下操作,通過unsafe類實現cas原子操作.
好了,上面就是unsafe的基本幾種使用,其也是aqs框架中cas操作的基礎.下面我們進行aqs相關學習.
AQS研究系列(二)--線程狀態和interrupt()、interrupted()、isInterrupted等方法學習
AQS研究系列(三)--AbstractQueuedSynchronizer源碼分析
㈣ aqs原理是什麼
aqs原理:
是一個用於構建鎖和同步器的框架,它能降低構建鎖和同步器的工作量,還可以避免處理多個位置上發生的競爭問題,在基於AQS構建的同步器中,只可能在一個時刻發生阻塞,從而降低上下文切換的開銷,並提高吞吐量。
相關信息:
AQS內部實現了兩個隊列,一個同步隊列,一個條件隊列。
同步隊列的作用是:當線程獲取資源失敗之後,就進入同步隊列的尾部保持自旋等待,不斷判斷自己是否是鏈表的頭節點,如果是頭節點,就不斷參試獲取資源,獲取成功後則退出同步隊列。
條件隊列是為Lock實現的一個基礎同步器,並且一個線程可能會有多個條件隊列,只有在使用了Condition才會存在條件隊列。
㈤ J.U.C|同步隊列(CLH)
在上篇我們聊到AQS的原理,具體參見 《J.U.C|帶你走進AQS的內心世界》 。
這篇我們來給大家聊聊AQS中核心同步隊列(CLH)。
同步隊列
一個FIFO雙向隊列,隊列中每個節點等待前驅節點釋放共享狀態(鎖)被喚醒就可以了。
AQS如何使用它?
AQS依賴它來完成同步狀態的管理,當前線程如果獲取同步狀態失敗時,AQS則會將當前線程已經等待狀態等信息構造成一個節點(Node)並將其加入到CLH同步隊列,同時會阻塞當前線程,當同步狀態釋放時,會把首節點喚醒(公平鎖),使其再次嘗試獲取同步狀態。
Node節點面貌?
CLH同步隊列的結構圖
這里是基於CAS(保證線程的安全)來設置尾節點的。
如上圖了解了同步隊列的結構, 我們在分析其入列操作在簡單不過。無非就是將tail(使用CAS保證原子操作)指向新節點,新節點的prev指向隊列中最後一節點(舊的tail節點),原隊列中最後一節點的next節點指向新節點以此來建立聯系,來張圖幫助大家理解。
源碼
源碼我們可以通過AQS中的以下兩個方法來了解下
addWaiter方法
先通過addWaiter(Node node)方法嘗試快速將該節點設置尾成尾節點,設置失敗走enq(final Node node)方法
enq
通過「自旋」也就是死循環的方式來保證該節點能順利的加入到隊列尾部,只有加入成功才會退出循環,否則會一直循序直到成功。
上述兩個方法都是通過compareAndSetHead(new Node())方法來設置尾節點,以保證節點的添加的原子性(保證節點的添加的線程安全。)
同步隊列(CLH)遵循FIFO,首節點是獲取同步狀態的節點,首節點的線程釋放同步狀態後,將會喚醒它的後繼節點(next),而後繼節點將會在獲取同步狀態成功時將自己設置為首節點,這個過程非常簡單。如下圖
設置首節點是通過獲取同步狀態成功的線程來完成的(獲取同步狀態是通過CAS來完成),只能有一個線程能夠獲取到同步狀態,因此設置頭節點的操作並不需要CAS來保證,只需要將首節點設置為其原首節點的後繼節點並斷開原首節點的next(等待GC回收)應用即可。
聊完後我們來總一下,同步隊列就是一個FIFO雙向對隊列,其每個節點包含獲取同步狀態失敗的線程應用、等待狀態、前驅節點、後繼節點、節點的屬性類型以及名稱描述。
其入列操作也就是利用CAS(保證線程安全)來設置尾節點,出列就很簡單了直接將head指向新頭節點並斷開老頭節點聯系就可以了。