semaphore
semaphore(信號量)應(yīng)用在并發(fā)領(lǐng)域編程,使用信號量能夠控制多線程執(zhí)行的并發(fā)執(zhí)行數(shù)。信號量機(jī)制建立在信號量模型之上,所以了解信號量模型才能了解信號量機(jī)制。
信號量模型
信號量模型由一個(gè)計(jì)數(shù)器,一個(gè)等待隊(duì)里,三個(gè)方法組成。信號量模型:

等待隊(duì)列:線程阻塞后線程存放的隊(duì)里。
計(jì)數(shù)器: 用于統(tǒng)計(jì)正在運(yùn)行或等待的線程數(shù)值。
三個(gè)方法:
- Init()計(jì)數(shù)器的初始值
- up()計(jì)數(shù)器的值加1。如果此時(shí)計(jì)數(shù)器值的<=0,則喚醒等待隊(duì)里的一個(gè)線程,并從等待隊(duì)列中移除
-down()計(jì)數(shù)器的值減1。如果此時(shí)的計(jì)數(shù)器值<0,則當(dāng)前的線程被阻塞。否則,線程繼續(xù)執(zhí)行。
三個(gè)方法必須保證原子性,并且原子性由信號量模型的實(shí)現(xiàn)的方法保證。比如Java里面信號量模型是由 java.util.concurrent.Semaphore實(shí)現(xiàn)的,Semaphore這三個(gè)方法都是原子操作。
信號量代碼分析
信號量模型實(shí)現(xiàn)代碼例子:
class Semaphore{
// 計(jì)數(shù)器
int count;
// 等待隊(duì)列
Queue queue;
// 初始化操作
Semaphore(int c){
this.count=c;
}
//
void down(){
this.count--;
if(this.count<0){
// 將當(dāng)前線程插入等待隊(duì)列
// 阻塞當(dāng)前線程
}
}
void up(){
this.count++;
if(this.count<=0) {
// 移除等待隊(duì)列中的某個(gè)線程 T
// 喚醒線程 T
}
}
}
-----------------------------------業(yè)務(wù)邏輯代碼----------------------------------------
static int count
// 初始化信號量
static final Semaphore s
= new Semaphore(1);
// 用信號量保證互斥
static void addOne() {
s.down();
try {
count+=1;
} finally {
s.up();
}
}
假設(shè)此時(shí)有兩個(gè)線程T1和T2同時(shí)訪問addOne()方法。由于down()方法是原子性的,所以只能有1個(gè)線程(T1)把計(jì)數(shù)器的值減到0,另一個(gè)線程(T2)把計(jì)數(shù)器減到-1。對于T1,計(jì)數(shù)器的值是0,大于等于0,所以繼續(xù)執(zhí)行。對于T2,計(jì)數(shù)器的值是-1,小于等于0,所以會把T2加入到阻塞隊(duì)列,然后當(dāng)前線程阻塞。T1執(zhí)行完畢之后,計(jì)數(shù)器加1,此時(shí)計(jì)數(shù)器的值是0,小于等于0,此時(shí)會移除等待隊(duì)列中的T2,然后在喚醒T2。
java信號量模型實(shí)現(xiàn)
java信號量模型由java.util.concurrent.Semaphore實(shí)現(xiàn)

- acquire()方法實(shí)現(xiàn)信號量模型的down()方法。
- relase()方法實(shí)現(xiàn)信號量模型的up()方法。
java通過提供acquire(int permits)方法減去指定數(shù)值的計(jì)數(shù)器值,以及通過提供tryAcquire(int permits, long timeout, TimeUnit unit)指定獲取信號量的超時(shí)時(shí)間。通過NonfairSync和FairSync類提供非公平和公平兩種獲取信號量實(shí)現(xiàn)機(jī)制。FairSync通過遍歷查看當(dāng)前線程是否在隊(duì)列頭節(jié)點(diǎn)來決定是否有資格去獲取信號量,NonfairSync通過CAS機(jī)制來搶占式來獲取信號量。