Semaphore,位于java.util.concurrent包下面
- Semaphore中管理著一組虛擬的許可,許可的初始數(shù)量可通過構(gòu)造函數(shù)來指定
new Semaphore(1);,執(zhí)行操作時可以首先獲得許可semaphore.acquire();,并在使用后釋放許可semaphore.release();。如果沒有許可,那么acquire方法將會一直阻塞直到有許可(或者直到被終端或者操作超時)。 - Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個線程,以保證它們能夠正確、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量。
- Semaphore分為單值和多值兩種,前者只能被一個線程獲得,后者可以被若干個線程獲得。
作用:可以用來控制同時訪問某個特定資源的操作數(shù)量,或者某個操作的數(shù)量。
下面使用Semaphore實(shí)現(xiàn)兩個例子:
- 互斥
大家都學(xué)過操作系統(tǒng),都知道互斥的概念,比較簡單的互斥實(shí)現(xiàn),比如PV操作,判斷資源,然后忙等實(shí)現(xiàn)互斥;上一篇也說過,忙等對CPU的消耗巨大,下面我們通過Semaphore來實(shí)現(xiàn)一個比較好的互斥操作:
假設(shè)我們公司只有一臺打印機(jī),我們需要對這臺打印機(jī)的打印操作進(jìn)行互斥控制:
首先是沒有使用Semaphore來實(shí)現(xiàn)互斥操作
public class Test {
public void print(String str) throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成,開始打印...");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
}
public static void main(String[] args) {
Test t = new Test();
for(int i = 0; i < 10 ; i++) {
new Thread() {
public void run() {
try {
t.print("hello");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
}
運(yùn)行結(jié)果:

沒有Semaphore
從結(jié)果可以看出,一個Thread還沒有打印完成,退出程序,別的Thread就進(jìn)入開始打印了,這樣必然沒有按照正確的流程來打印。
使用Semaphore來控制互斥訪問數(shù)
import java.util.concurrent.Semaphore;
/**
* 使用Semphore實(shí)現(xiàn)互斥訪問打印機(jī)
* @author ghw
*
*/
public class Test {
//定義初始值為1的信號量
private Semaphore semaphore = new Semaphore(1);
//模擬打印機(jī)打印操作
public void print(String str) throws InterruptedException {
//請求許可
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成,開始打印...");
//模擬打印時間
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
//釋放許可
semaphore.release();
}
public static void main(String[] args) {
Test t = new Test();
//開啟10個線程執(zhí)行打印任務(wù)
for(int i = 0; i < 10 ; i++) {
new Thread() {
public void run() {
try {
t.print("hello");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
}
運(yùn)行結(jié)果:

使用Semphore
從結(jié)果可以看出,多個線程雖然是無序執(zhí)行的,但每個線程執(zhí)行完成后退出程序才開始執(zhí)行下一個線程,這樣就不會亂。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
public class ConnectPool {
private final List<Conn> pool = new ArrayList<Conn>(3);
private Semaphore semaphore = new Semaphore(3);
public ConnectPool() {
pool.add(new Conn());
pool.add(new Conn());
pool.add(new Conn());
}
public Conn getConn() throws InterruptedException {
semaphore.acquire();
Conn c = null ;
synchronized (pool)
{
c = pool.remove(0);
}
System.out.println(Thread.currentThread().getName()+" 獲得一個連接 " + c);
return c ;
}
public void release(Conn c) {
pool.add(c);
System.out.println(Thread.currentThread().getName()+" 釋放了一個連接 " + c);
semaphore.release();
}
public static void main(String[] args) {
ConnectPool pool = new ConnectPool();
new Thread() {
public void run() {
try {
Conn c = pool.getConn();
Thread.sleep(3000);
pool.release(c);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
for(int i=0;i<5;i++) {
new Thread() {
public void run() {
try {
Conn c = pool.getConn();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}
}
}
運(yùn)行結(jié)果:

運(yùn)行結(jié)果3
先讓Thread-0持有一個連接3秒,然后瞬間讓3個線程再去請求分配連接,造成Thread-3一直等到Thread-0對連接的釋放,然后獲得連接。
通過兩個例子,基本已經(jīng)了解了Semaphore的用法,這里的線程池例子只是為了說明Semaphore的用法,真實(shí)的實(shí)現(xiàn)代碼比這復(fù)雜的多,而且可能也不會直接用Semaphore。