Semaphore實現Andoird版原始碼剖析

來源:互聯網
上載者:User

標籤:style   java   使用   io   strong   for   ar   2014   art   

Semaphore是一個計數的訊號量。從概念上來說,訊號量維持一組許可(permits)。acquire方法在必須的時候都會堵塞直到有一個許可可用,然後就會拿走這個許可。release方法加入一個許可,會有可能釋放一個堵塞中的擷取者(acquirer)。然而,Semaphore沒有使用真實的許可對象,僅僅是保持一個可用計數而且採取對應的行為。

訊號量一般用於限制能夠訪問一些(物理上或者邏輯上)的資源的並發線程數。

    訊號量初始化為1的時候,意味著它最多僅僅有一個同意可用,這樣就能作為相互排斥獨佔鎖使用。這樣的很多其它地被稱為二進位訊號量(binary semaphore),由於它僅僅有兩個狀態:一個許可可用,或者0個許可可用。當使用這樣的方式的時候,二進位訊號量就有這樣的屬性(不像大部分鎖的實現):鎖能夠被擁有者(就如訊號量沒有擁有者的概念)之外的另外線程釋放。這樣的屬性在某些特殊的上下文中非常實用,比如死結恢複。
    類的建構函式可選地接受一個fairness參數。當設為false的時候,該類就不會保證線程擷取許可的順序。特別地,插隊是同意的,也就是說,線程調用acquire方法能夠在另外的等待線程之前分配許可————邏輯上新線程會把自己放在等待線程隊列頭。當fairness設為true,訊號量就保證調用acquire方法的線程會以它們調用方法的處理順序來擷取許可(FIFO)。注意FIFO的順序決定特指在這些方法的內部運行點。因此,有可能一條線程在另外一條線程之前調用了acquire方法,但實際順序會在另外一條線程之後,相同也適用於函數返回的先後順序。相同要注意非逾時版本號碼tryAcquire方法不會遵循fairness設定,會立即擷取不論什麼可用的許可。
    一般來說,訊號量用來控制資源訪問的話,就應該被初始化為公平(fairness設為true),這樣能夠確保沒有線程會在訪問資源的時候餓壞。當使用在其它同步控制的情況下使用訊號量,非公平的輸送量優勢一般優於公平的考慮。

    事實上整體上來看,fairness參數以及狀態量的概念非常接近AQS(AbstractQueuedSynchronizer)提供的功能,因此大家也應該猜到Semaphore的內部實現也是通過一個繼承AQS的內部類實現介面功能。接下來我們細緻看看內部的實現。

詳細實現

    先來看看Semaphore的建構函式:
    public Semaphore(int permits) {        sync = new NonfairSync(permits);    }    public Semaphore(int permits, boolean fair) {        sync = fair ? new FairSync(permits) : new NonfairSync(permits);    }
    能夠看到建構函式與ReentrantLock實作類別似,都是依照fair參數分配建立不同的鎖類,再來看看Semaphore的acquire和release的介面實現
    public void acquire() throws InterruptedException {        sync.acquireSharedInterruptibly(1);    }    public void release() {        sync.releaseShared(1);    }
    能夠看到acquire和release的實現都是調用內部類Sync的方法實現,當然了,這些方法也就是AQS提供出來的擷取和釋放共用鎖定介面。接下來看看整個實現裡最基本的內部類Sync的相關實現:
    abstract static class Sync extends AbstractQueuedSynchronizer {        private static final long serialVersionUID = 1192457210091910933L;        Sync(int permits) {            setState(permits);        }        final int nonfairTryAcquireShared(int acquires) {            for (;;) {                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }        protected final boolean tryReleaseShared(int releases) {            for (;;) {                int current = getState();                int next = current + releases;                if (next < current) // overflow                    throw new Error("Maximum permit count exceeded");                if (compareAndSetState(current, next))                    return true;            }        }        //省略一些次要方法    }    /**     * 非公平版本號碼     */    static final class NonfairSync extends Sync {        private static final long serialVersionUID = -2694183684443567898L;        NonfairSync(int permits) {            super(permits);        }        protected int tryAcquireShared(int acquires) {            return nonfairTryAcquireShared(acquires);        }    }    /**     * 公平版本號碼     */    static final class FairSync extends Sync {        private static final long serialVersionUID = 2014338818796000944L;        FairSync(int permits) {            super(permits);        }        protected int tryAcquireShared(int acquires) {            for (;;) {                if (hasQueuedPredecessors())                    return -1;                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }    }
    為了方便瞭解主要邏輯,Sync類省略掉一些次要的方法。非公平版本號碼NonfairSync類和公平版本號碼FairSync類都繼承於Sync類,Sync類繼承於AQS類,NonfairSync和FairSync類都有相同的tryReleaseShared實現,僅僅只是在tryAcquireShared實現上有略微不同。
    先來看看非公平類NonfairSync實現。tryAcquireShared調用的是Sync類的nonfairTryAcquireShared方法,方法的實現相當簡單,僅僅是在迴圈內推斷當前鎖狀態值減去請求值acquires後,假設remaining < 0(則表示此次acquire失敗,直接返回負值remaining就可以)或者remaining >=0 時,compareAndSetState成功(表示此次acquire成功,直接返回大於等於0的remaining就可以),假設CAS失敗,則繼續迴圈重試,直到當中一種情況發生。
    再來看看公平類FairSync的實現。tryAcquireShared直接被重寫,與非公平類版本號碼對照,添加了hasQueuedPredecessors的推斷,該方法在AQS中表示是否有結點在當前的等待隊列前排在自己前面,假設返回true,則表示當前線程須要進入等待隊列,直接返回-1表示acquire失敗。
    tryReleaseShared的實現也非常easy,也是一個迴圈裡不斷CAS把鎖狀態添加請求的releases就可以。
    Semaphore還有其他一些輔助方法,事實上現也都是簡單地調用內部類Sync的方法,這裡便不再贅述。

總結
    整體來看,AQS的鎖狀態值就等於Semaphore的許可量,acquire的實現就是把當前鎖狀態值,也就是許可量減去相應值,release的實現就是把鎖狀態值添加相應值就可以。整個實現結構和ReentrantLock類似,但沒有了重入的邏輯,並且實現更是相對簡單,理解起來應該沒有難度。

Semaphore實現Andoird版原始碼剖析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.