從Semaphore使用到AQS源碼學(xué)習(xí)

Semaphore簡介

Semaphore信號量,主要作用就是做資源流控,舉例飯店一共可以容納5個人吃飯,當(dāng)飯店中已經(jīng)進(jìn)入了5個人,那么第6個人只能在外面等待里面的人吃完之后才能就餐.Semaphore 就好像是飯店的招待.穗人員的進(jìn)出進(jìn)行登記管理.

Semaphore 簡單使用

    //兩個信號指示器,分別表示飯店還有多少就餐位置,和已經(jīng)就餐的人數(shù)
private final Semaphore kong = new Semaphore(5); //空余位置5個
private final Semaphore person = new Semaphore(0); //正在就餐的人數(shù)    
    //存放就餐工具的池子(餐具,桌椅)
private static LinkedList<EatTool> pool = new LinkedList<EatTool>();

static {
    System.out.println("飯店開張嘍!!!");
    for (int i = 0; i < 5; i++) {
        pool.addLast(new EatTool());
    }
}

   /*吃飽了*/
public void returnEatTool(EatTool eattool) throws InterruptedException {
    if(eattool!=null) {
        person.acquire();
        synchronized (pool) {
            pool.addLast(eattool);
        }
        kong.release();
    }
}

/*準(zhǔn)備就餐*/
public Eattool takeEatTool() throws InterruptedException {
    kong.acquire();
    Eattool eattool;
    synchronized (pool) {
        eattool = pool.removeFirst();
    }
    person.release();
    return eattool;
}

特別說明的就是之所以聲明兩個信號量就是因?yàn)镾emaphore在沒有資源被使用的時候,有人調(diào)用release方法也是會是state狀態(tài)計(jì)數(shù)累加,也就是憑空有可能多出很多的桌椅,這樣就不能保證餐廳的高效運(yùn)行,資源不能合理的利用

Semaphore原理分析

看源碼我們可以清楚的知道Semaphore是使用AQS實(shí)現(xiàn)的 ,AQS是基于雙向鏈表和Syn類(掌控同步鎖的細(xì)節(jié))
這里就貼一段源碼進(jìn)行分析

    /**
    * Synchronization implementation for semaphore.  Uses AQS state
    * to represent permits. Subclassed into fair and nonfair
    * versions.
    */
    abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;

    Sync(int permits) {
        setState(permits);
    }

    final int getPermits() {
        return getState();
    }

    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;
        }
    }

    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }

    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}

/**
 * NonFair version(非公平鎖的實(shí)現(xiàn))
 */
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);
    }
}

/**
 * Fair version(公平鎖的實(shí)現(xiàn))
 */
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;
        }
    }
}

這里就是Syn鎖的全部細(xì)節(jié).
在我們的實(shí)際使用的過程中最基本的方法就是這兩個方法了accquire()和release()了.一個負(fù)責(zé)請求(我要吃飯).一個負(fù)責(zé)釋放(我吃飽了)


image.png

先去吃飯accquire()

一層層進(jìn)入源碼我們就不難發(fā)現(xiàn)

if (tryAcquireShared(arg) < 0) //嘗試請求  state計(jì)數(shù)-1
        doAcquireSharedInterruptibly(arg); //失敗后添加到等待隊(duì)列中
}

核心的部分也就是這一段了
tryAcquireShared() 就是我們根據(jù)AQS中提供的模板方法實(shí)現(xiàn)的細(xì)節(jié)getState()中是讀取AQS類中的State記錄的狀態(tài),當(dāng)state中儲存的int值小于零的時候請求的線程將在doAcquireSharedInterruptibly()準(zhǔn)備進(jìn)行阻塞,這里用到是對AQS中的雙向節(jié)點(diǎn)列表進(jìn)行的操作了,假如state的值大于零表示飯店還有位置可以就餐.這里用到了一些AQS的操作高效的保證線程安全,不了解的童鞋自行補(bǔ)課吧.

吃飽了release()

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { //嘗試釋放資源 state 計(jì)數(shù)+1
        doReleaseShared(); //通知等待隊(duì)列  可以釋放鎖了
        return true;
    }
    return false;
}

簡單的解析就是正在就餐的人就餐完畢后會通過release()方法通知后面等待的人來就餐.

最后

使用AQS我們可以開發(fā)出各式各樣的滿足的我們要求的鎖 AQS的理解還是很重要的,本文寫很膚淺,部分邏輯和描述不是很清楚,筆者也是鍛煉一下表達(dá)能力.具體細(xì)節(jié)請尊重源碼吧.千言萬語源碼才是最好的說明文檔.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容