Semaphore,位于java.util.concurrent包下面
- Semaphore中管理著一組虛擬的許可,許可的初始數(shù)量可通過構(gòu)造函數(shù)來指定
new Semaphore(1);
跨蟹,執(zhí)行操作時(shí)可以首先獲得許可semaphore.acquire();
熙尉,并在使用后釋放許可semaphore.release();
。如果沒有許可车柠,那么acquire方法將會一直阻塞直到有許可(或者直到被終端或者操作超時(shí))剔氏。 - Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個(gè)線程竹祷,以保證它們能夠正確谈跛、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量塑陵。
- Semaphore分為單值和多值兩種感憾,前者只能被一個(gè)線程獲得,后者可以被若干個(gè)線程獲得令花。
作用:可以用來控制同時(shí)訪問某個(gè)特定資源的操作數(shù)量阻桅,或者某個(gè)操作的數(shù)量。
下面使用Semaphore實(shí)現(xiàn)兩個(gè)例子:
- 互斥
大家都學(xué)過操作系統(tǒng)兼都,都知道互斥的概念嫂沉,比較簡單的互斥實(shí)現(xiàn),比如PV操作扮碧,判斷資源趟章,然后忙等實(shí)現(xiàn)互斥杏糙;上一篇也說過,忙等對CPU的消耗巨大蚓土,下面我們通過Semaphore來實(shí)現(xiàn)一個(gè)比較好的互斥操作:
假設(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é)果:
從結(jié)果可以看出谅河,一個(gè)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)備完成蠕嫁,開始打印...");
//模擬打印時(shí)間
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個(gè)線程執(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é)果:
從結(jié)果可以看出,多個(gè)線程雖然是無序執(zhí)行的剃毒,但每個(gè)線程執(zhí)行完成后退出程序才開始執(zhí)行下一個(gè)線程病袄,這樣就不會亂。
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()+" 獲得一個(gè)連接 " + c);
return c ;
}
public void release(Conn c) {
pool.add(c);
System.out.println(Thread.currentThread().getName()+" 釋放了一個(gè)連接 " + 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é)果:
先讓Thread-0持有一個(gè)連接3秒赘阀,然后瞬間讓3個(gè)線程再去請求分配連接益缠,造成Thread-3一直等到Thread-0對連接的釋放,然后獲得連接基公。
通過兩個(gè)例子幅慌,基本已經(jīng)了解了Semaphore的用法,這里的線程池例子只是為了說明Semaphore的用法轰豆,真實(shí)的實(shí)現(xiàn)代碼比這復(fù)雜的多胰伍,而且可能也不會直接用Semaphore。