AQS是JUC當中最核心的部分,大部分多線程講解,都不會詳細講AQS,AQS的源代碼,要看明白仍是有點困難的。可是一旦看明白了,結構仍是蠻清晰的。這裏咱們把AQS拆開,分紅幾部分來說,就會很清楚了。首先先從內部類Nde講起php
上源代碼:java
static final class Node { static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
這個NODE類是java.util.concurrent.locks.AbstractQueuedSynchronizer的內部類。主要實現了一個鏈表的數據結構中的節點。先不談這個鏈表是作什麼的,咱們先看看這個節點是怎麼構造的。數據結構
首先,這個節點有四個狀態 ,其中三在Node類中已經明肯定義,另一個狀態爲初始狀態,值爲0多線程
static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2;
1 :當前節點被取消或者中斷
併發
-1:下一個節點須要被釋放this
-2:當前節點正處在等待隊列中spa
0 :不屬於任何一種.net
其次,這個節點有兩種模式,其中SHARED是共享模式,EXCLUSIVE是獨佔模式。
線程
static final Node SHARED = new Node(); static final Node EXCLUSIVE = null;
等一下再介紹這兩個模式。先把數據結構看完。指針
而後是成員變量:
volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter;
volatile int waitStatus:當前節點的狀態,一共四種
volatile Node prev:鏈表的前一個節點
volatile Node next:鏈表的後一個節點
volatile Thread thread:這個節點所處的線程
Node nextWaiter:這個節點等待的模式(共享模式和獨佔模式)
接下去的兩個方法和三個構造方法很是簡單,就不講了。
問題1:這個鏈表是如何工做的?
用一張圖表示隊列的工做方式:
每一個節點都擁有一個指針,知道本身的前一個節點
每一個節點都有一個狀態位,表示是否佔用資源
每一個節點都要完成自旋判斷上一個節點的狀態(圖中while循環),才能真正的執行本身的邏輯
當邏輯執行完,會修改本身的節點的狀態,放開下個節點的自旋
新插入的節點,永遠在隊尾
上圖已經很是清楚的描述了這個隊列的工做方式。這就是CLH鎖的原理。其實實現鎖還有幾種不一樣的方式。(點擊瞭解更多鎖的實現原理和比較)
因爲一個線程的狀態不止兩種,因此Node的狀態實現,並無用True/False表示。而是用了waitStatus表示。另外爲了操做的方便,Node並無使用單向鏈表,而是雙向鏈表。這樣操做起來會方便不少。
以下圖:
每一個節點都擁有一個指針,知道本身的前一個節點和後一個節點
每一個節點都有一個狀態位,表示是否佔用資源
每一個節點都要完成自旋判斷上一個節點的狀態(圖中while循環),才能真正的執行本身的邏輯
當邏輯執行完,會修改本身的節點的狀態,放開下個節點的自旋
新插入的節點,永遠在隊尾。隊尾節點標記新插入節點
被取消或者中斷的線程節點,會從鏈表中斷開,被刪除
還記得在講LockSupport(點擊查看源碼)中是如何掛起和恢復一個線程的嗎?Thread對象就是作這個的。
到此爲止,咱們的圖與Node類的結構是否是已經很像了呢?
問題2:共享模式和獨佔模式
共享模式和獨佔模式是兩種不一樣的鎖定資源的方式。
最經典的舉例就是對共享文件的操做。當第一個,第二個用戶都請求讀取文件內容時,並不會阻塞用戶的讀取行爲,而且能夠併發的讀取。
可是當第三個用戶請求修改的時候,就會獲取一個獨佔鎖。一旦獲取了獨佔鎖,全部共享鎖的獲取或者獨佔鎖的獲取都會被阻塞,一直等
到獨佔鎖被釋放纔會解除阻塞。
實際在JUC中讀寫鎖也是一個很典型的共享與獨佔的例子。