天天看點

Java并發架構——同步狀态的管理

整個AQS架構核心功能都是圍繞着其32位整型屬性state進行,一般可以說它表示鎖的數量,對同步狀态的控制可以實作不同的同步工具,例如閉鎖、信号量、栅欄等等。為了保證可見性此變量被聲明為volatile,保證每次的原子更新都将及時反映到每條線程上。而對于同步狀态的管理可以大體分為兩塊,一是獨占模式的管理,另外是共享模式的管理。通過對這兩種模式的靈活變換可以實作多種不同的同步器,如下圖,對state的控制可以看成一個管道,管道的大小決定了同時通過的線程,獨占模式好比寬度隻容許一個線程通過的管道,在這種模式下線程隻能逐一通過管道,任意時刻管内隻能存在一條線程,這便形成了互斥效果。而共享模式就是管道寬度大于1的管道,可以同時讓n條管道通過,吞吐量增加但可能存在共享資料一緻性問題。(注意:兩種模式的讨論忽略了隊列的管理邏輯,實際上CLH Node的引入是為了優化競争帶來的性能問題,不影響同步狀态管理的探讨)

Java并發架構——同步狀态的管理

圖2-5-9-5 獨占模式與共享模式

如何通過state實作獨占模式和共享模式?在此之前先了解AQS架構中相關的getState、setState、compareAndSetState三個操作state的基本方法,前兩個方法是普通的擷取設定方法,其必須保證不存在資料競争的情況下使用,compareAndSetState方法則提供了CAS方式的硬體級别的原子更新。兩種模式就是通過這些方法對state操作實作不同同步模式,下面給出最簡單的實作。

 獨占模式

public boolean tryAcquire(int acquires) {

if (compareAndSetState(0, 1)) {

return true;

}

return false;

protected boolean tryRelease(int releases) {

setState(0);

多條線程通過tryAcquire嘗試把state變量改為1,由于CAS算法的保證,最終有且僅有一條線程成功修改state,修改成功的線程代表擷取鎖成功,将擁有往下執行的權利,進入管道。當執行完畢退出管道時執行tryRelease嘗試把state變量改為0,讓出管道,此處由于不存線上程競争是以可直接使用setState,接着其他未通過的線程繼續重複嘗試。

 共享模式

public int tryAcquireShared(int interval) {

for (;;) {

int current = getState();

int newCount = current - 1;

if (newCount < 0 || compareAndSetState(current, newCount)) {

return newCount;

public boolean tryReleaseShared(int interval) {

int newCount = current + 1;

if (compareAndSetState(current, newCount)) {

與獨占模式不同的是對state的管理及判斷條件,獨占模式state的值隻能為0或1,而共享模式的state是可以被出事換成任意整數,一般初始值表示提供一個同時n條線程通過的管道寬度,這樣一來,多條線程通過tryAcquireShared嘗試将state的值減去1,成功修改state後就傳回新值,隻有當新值大于等于0才表示擷取鎖成功,擁有往下執行的權利,進入管道。在執行完畢時線程将調用tryReleaseShared嘗試修改state值使之增加1,表示我已經執行完了并讓出管道的通道供後面線程使用,需要說明的是與獨占模式不同,由于可能存在多條線程并發釋放鎖,是以此處必須使用基于CAS算法的修改方法,修改成功後其他線程便可繼續競争鎖。

ASQ架構提供了對同步狀态state的基本操作,了解了兩種模式對state操作開發者可能很自由地自定義自己的同步器。實際中AQS架構在提供state狀态管理接口的同時也将維護等待隊列的工作,兩項工作被封裝成一個模闆,規定了工作流程,工作流程包括什麼條件下加入等待隊列、什麼條件移除等待節點、如何操作等待隊列、需不需要阻塞、支不支援中斷等等,對外僅僅提供state狀态操作接口供開發者自定義,而隊列的維護工作已經綁定在模闆中,無需你自己動手。

<a target="_blank" href="https://item.jd.com/12185360.html">點選訂購作者書籍《Tomcat核心設計剖析》</a>