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é)釋放(我吃飽了)

先去吃飯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é)請尊重源碼吧.千言萬語源碼才是最好的說明文檔.