1. 程式人生 > 實用技巧 >對於AQS的理解

對於AQS的理解

1、JUC包中的 CountDownLatch、CyclicBarrier、ReentrantLock和Semaphore都是基於AQS(AbstractQuenedSynchronizer)實現的

在ReentrantLoc這個元件裡,stste表示獲取鎖的執行緒數,假如state=0,表示還沒有執行緒獲取鎖,1表示有執行緒獲取了鎖。大於1表示重入鎖的數量。

繼承:子類通過繼承並通過實現它的方法管理其狀態(acquire和release方法操縱狀態)。

可以同時實現排它鎖和共享鎖模式(獨佔、共享),站在一個使用者的角度,AQS的功能主要分為兩類:獨佔和共享。它的所有子類中,要麼實現並使用了它的獨佔功能的api,要麼使用了共享鎖的功能,而不會同時使用兩套api,即便是最有名的子類ReentrantReadWriteLock也是通過兩個內部類讀鎖和寫鎖分別實現了兩套api來實現的

2、AQS底層實現了一個FIFO的佇列。底層的資料結構是一個雙向連結串列;

3、AQS核心思想

如果被請求的共享資源空閒,則將當前請求資源的執行緒設定為有效的工作執行緒,並且將共享資源設定為鎖定狀態。如果被請求的共享資源被佔用,那麼就需要一套執行緒阻塞等待以及被喚醒時鎖分配的機制,這個機制AQS是用CLH佇列鎖實現的,即將暫時獲取不到鎖的執行緒加入到佇列中。


4、CLH佇列(FIFO)

CLH佇列是一個虛擬佇列,沒有佇列例項只有節點與節點之間的關係。

AQS使用一個int成員變數來表示同步狀態,通過內建的FIFO佇列來完成獲取資源執行緒的排隊工作。AQS使用CAS對該同步狀態進行原子操作實現對其值的修改。

當一個執行緒嘗試獲取鎖失敗後,就會把失敗資訊封裝成一個node節點嘗試加入這個同步佇列。因為可能有多個執行緒都想加入佇列尾部,所以加入佇列是採用了cas + volatile。

加入佇列後獲取鎖的操作:佇列裡面的節點會觀察pre節點即前置節點的狀態,如果是不是頭結點就會阻塞,如果前面一個節點是頭結點就會被喚醒一直迴圈嘗試獲取鎖。

5、VarHandle

varHandle是jdk9之後加入的,handle的意思是控制代碼,VARHandle指的就是引用變數指向具體物件的那個引用

varHandle可以直接給變數賦值,並且做一些原子操作

public class Demo {
    int x = 10;
    private static VarHandle varHandle;

    static {
        try {
            varHandle = MethodHandles.lookup().findVarHandle(Demo.class, "x", int.class);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Demo demo = new Demo();

        //獲取變數值
        System.out.println((int)varHandle.get(demo));

        //為變數賦值
        varHandle.set(demo, 1);
        System.out.println(demo.x);

        //進行原子操作
        varHandle.getAndAdd(demo,3);
        System.out.println(demo.x);
    }
}