前面已經可以了解到AQS的設計,接下看一下具體的使用和實現:
1、java.util.concurrent 的結構:
曾經有一張圖可以十分清楚地展示java.util.concurrent的結構,我也借來用一下,有助于理清楚整個大的結構,接下去主要是對各個部分的具體實現進行分析,首先還是從鎖說起;
?
?
2、lock部分實現的類圖:
上圖是整個AQS的類圖,紅色框類表示JDK文檔里面自帶的一個簡單使用ASQ的互斥鎖的實現;接下去我們主要從AQS本身和這個簡單的實現來解讀源碼;當然這里完全可以換為ReentrantLock等我們常使用的鎖;只不過他們們本身有更加復雜的策略,但是作為AQS的使用者來說,完全是一樣的;
?
2.1、父類AbstractOwnableSynchronizer:從類圖中可以發現這個抽象類只有一個Thread類型的成員變量exclusiveOwnerThread;用來存放獨占模式時當前的線程;
?
2.2、Node類:這個類定義了一系列的靜態的常量,類圖的中的注釋可以看到主要的一部分,如CANCELLED、SIGNAL等,這4個int常量加上0用來作為waitStatus可能的值,用來表示當前線程的狀態;另外還有兩個Node類型的常量SHARED、EXCLUSIVE,SHARED賦值為了一個空的Node對象,EXCLUSIVE=null;這個兩個常量用來作為nextWaiter對象可能的取值,表示下一個在條件隊列上的node,因為條件隊列只有在互斥模式下有意義,所以如果是共享模式等于一個常量,即是SHARED,而互斥模式下新增第一個節點的時候,由于還沒有其他線程在該條件上阻塞,所以初始化為兩一個常量EXCLUSIVE,也就是null;
? ? ? 另外剩下就是一個指向對應線程的Thread的對象,和指向前一個和后一個節點的Node類型的對象;
? ? ? Node類有兩個重要的方法,一個是isShared(),很明顯是判斷當前線程的同步模式,根據前面對nextWaiter變量的說明,很容易想到實現只有一行代碼:
?
final boolean isShared() { return nextWaiter == SHARED; }
?兩外一個是predecessor(),返回當前節點的前一個節點,實現也很簡單:返回prev的值,只是增加為null的判斷;
?
final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; }
你一定會很奇怪:為什么要單獨提供predecessor,而沒有提供對應的獲取next的方法呢?我想主要有兩個原因:1、在論文里面也提到,next的路徑只是作為一個優化路徑。如果一個節點的next判斷是null,是不能相信的還必須通過從尾部通過prev域反向查找(因為不能實現雙向鏈表的無鎖的原子插入)。2、在后面的使用場景中可以發現,其實是把跑出異常作為了結束循環的條件,統一到這里來可以使使用的代碼簡單整潔。
?整個Node類是static、final的,并且是AQS的內部類;
?
2.3、AQS類,這個類是整個lock框架的核心,代碼量也比較大,將在下一節詳細的介紹;
?
未完待續。。。