semaphore翻譯為信號(hào)量,它用來(lái)做什么用呢?——看JDK文檔:
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource. For example, here is a class that uses a semaphore to control access to a pool of items:
由這段英文可知,semaphore是限制線程數(shù)量的,原因往往是資源有限。接著JDK文檔舉了一個(gè)例子铐姚,由semaphore來(lái)限制對(duì)象池的訪問。對(duì)象池的大小是固定的肛捍,其中資源的訪問需要限制隐绵。
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
對(duì)象池中的對(duì)象數(shù)量是100,所以semaphore的初始值也是100拙毫。獲取對(duì)象之前依许,必須通過semaphore的通過。getNextAvailableItem()被synchronized修飾缀蹄,這是防止并發(fā)情況下峭跳,返回同一個(gè)對(duì)象膘婶,并且標(biāo)記對(duì)象的使用狀態(tài)。歸還對(duì)象池中對(duì)象時(shí)蛀醉,需要增加semaphore的值availablePermits()悬襟。
這段代碼也可以看到Pool的設(shè)計(jì)思想。
想到2016年遇到的一道面試題拯刁,假設(shè)有N個(gè)線程古胆,依次打印0, 1, 2, N-1, N, N+1, N+2 ...
2N-1……
審題可知,這道題主要考察多線程之間的同步筛璧。多個(gè)線程之間循環(huán)同步,而且輸出保持有序性惹恃,可知每次輸出只能有一個(gè)線程打印夭谤,這里剛好遇到semaphore.
class Worker4 implements Runnable {
private int x;
private int co;
Semaphore semaphore;
public Worker4(int x, int co, Semaphore semaphore) {
this.x = x;
this.co = co;
this.semaphore = semaphore;
}
@Override
public void run() {
while (x < 1000) {
try {
semaphore.acquire();
if (x == ConditionTest.n + 1) {
System.out.println(Thread.currentThread().getName() + "\t" + (++ConditionTest.n));
x += co;
}
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(1, false);
System.out.println(semaphore.availablePermits());
// n 是線程數(shù)量
int n = ThreadLocalRandom.current().nextInt(10, 20);
for (int i = 0; i < n; i++) {
Thread thread = new Thread(new Worker4(i, n, semaphore));
thread.start();
}
}
2019-07-18
為什么要用兩個(gè)“鎖”?這個(gè)問題巫糙,我很早就覺察了±嗜澹現(xiàn)在重看代碼才明白。
內(nèi)部鎖是必須的参淹,因?yàn)橐驗(yàn)間et 和 put 都不是原子操作醉锄。
信號(hào)量是為了控制線程數(shù)量的,這樣只會(huì)讓少量的線程去競(jìng)爭(zhēng)內(nèi)部鎖浙值。
如果沒有信號(hào)量恳不,那所有的線程都會(huì)搶占內(nèi)部鎖,這無(wú)疑是不合理的开呐。
所以烟勋,信號(hào)量有限流的作用。