AQS原理解析

2021-09-17 18:43:19 字數 2790 閱讀 8067

aqs(abstractqueuesynchronizer) 即 抽象佇列同步器, 聽起來非常拗口, 沒有關係,暫且先記住佇列、同步這兩個關鍵字,需要把它理解為乙個框架,即用來實現多執行緒訪問共享資源的同步框架。比如reentrantlock、semaphore、countdownlatch等等的實現都依賴於這個同步框架。

aqs的原理是clh鎖的乙個變種,根據某大佬的部落格講解,一般的自旋鎖會將併發所有的競爭集中在乙個標誌位當中,而clh鎖的思想是將競爭資源的執行緒排成乙個佇列,每個後進來的執行緒僅在前乙個標誌位上進行自旋,當頭節點釋放鎖後,後繼執行緒會在自旋時發現這個變化,從而嘗試獲取鎖來進入資源臨界區。通過這種設計來最大化的減少cpu快取的失效次數,如果是普通的自旋鎖,每次標誌位變化都會引起所有的cpu快取失效然後去記憶體當中重新讀取相應的值,通過clh鎖,將這個粒度縮小到了單個cpu範圍內。

在講可重入鎖之前這裡順帶給乙個不可重入鎖的實現demo;

public class unreentrantlock }}

public void unlock()

}

記住, 任何cas演算法往底層走最終都會跑到unsafe這個類來呼叫native方法,  這個demo還是比較好看懂的, 通過乙個死迴圈來自旋執行緒,在同一執行緒第二次進入該方法時,由於owner的值已經為current了,但是第二次自旋的expect value 仍為null,這樣就會導致if語句的判斷一直為false從而一直死迴圈導致後面的unlock()方法沒法執行。

進入正題, 先看reen;trantlock的lock方法

/**       如果state為0設定當前執行緒為獨佔(互斥)鎖, 否則的話嘗試獲取鎖

*/final void lock()

acquire(1)的原始碼如下:

/* 先嘗試獲取鎖,  失敗後就新增乙個獨佔模式的節點進入clh佇列當中,如果在等待過程中線程被中斷過,它不會響應而是在獲取資源後再進行自我中斷selfinterrupt();*/

public final void acquire(int arg)

接下來就得看tryacquire和acquirequeued、addwaiter的實現了,根據reentrantlock預設的非公平鎖來看原始碼會一直走到如下**中

/* 用nextc來計算重入次數, 那麼為什麼會小於0呢, 在int值大於2147483647後,這個值就會為-2147483648

*/final boolean nonfairtryacquire(int acquires)

}else if (current == getexclusiveownerthread())

return false;

}

再看後面的方法 先看addwaiter

/*  如果尾節點不為空那麼將新的節點通過cas入隊設定成尾節點並返回,否則執行enq方法

*/private node addwaiter(node mode)

}enq(node);

return node;

}

檢視enq

/*  這裡的**還是比較容易看懂的, 比如說多個執行緒同時enq, 那麼只有乙個執行緒通過cas

被初始化成tail和head節點,然後另外的執行緒繼續死迴圈執行else條件的cas操作來進行尾節點替換,

每次cas都會成功乙個執行緒, 根據執行緒衝突個數來決定迴圈次數 。

*/private node enq(final node node) else }}

}

再然後就是acquirequeued方法

/* 先獲取當前節點的前驅節點,如果前驅節點為頭節點那麼再嘗試一次獲取鎖,

成功後把自己設為頭節點並把p相關引用置空方便gc,否則的話需要執行緒需要根據shouldparkafte***iledacquire考慮是否將自己阻塞

*/final boolean acquirequeued(final node node, int arg)

if (shouldparkafte***iledacquire(p, node) &&

parkandcheckinterrupt())

interrupted = true;

}} finally

}

看shouldparkafte***iledacquire

/*

判斷前驅節點的waitstatus是否為signal,是則對該執行緒進行阻塞,如果前驅節點cancelled,

則需要進行迴圈找到第乙個不為cancelled的節點作為自己的前驅,然後通過上一層方法的死迴圈

又會再執行一次shouldparkafte***iledacquire,這個時候會將前驅節點設定成signal,

然後又通過上一層方法的死迴圈,再執行過來此時就可以返回true了, 然後執行緒阻塞,

然後還是外層死迴圈會不斷判斷該節點的前驅節點是否為頭節點,一旦為頭節點又嘗試獲取鎖

AQS使用和原理解析

使用總結 aqs的基本原理 concurrenthashmap 獨佔式獲取同步狀態,試著獲取,成功返回true,反之為false protected boolean tryacquire int arg 獨佔式釋放同步狀態,等待中的其他執行緒此時將有機會獲取到同步狀態 protected boole...

深入理解AQS原始碼解析一

三 最後小結一下 一 概念 我們談到併發,就不得不談reentrantlock鎖 而談到reentrantlock鎖,不得不談一下abstractqueuedsynchronized aqs 類如其名,抽象的佇列式的同步器,aqs定義了一套多執行緒訪問共享資源的同步器框架,許多同步類實現都依賴於它,...

AQS原始碼解析

公平鎖 fairsync 核心方法 public final void acquire int arg tryacquire arg 方法 protected final boolean tryacquire int acquires 重入鎖 state 1 else if current gete...