理解semaphore

Semaphore(信號量)

信號量可以簡單的概括為:一個計數(shù)器,一個等待隊列,三個方法。在信號量模型里,計數(shù)器和等待隊列對外是透明的,所以只能通過信號量模型提供的三個方法來訪問它們,這三個方法分別是:init()、down()和up()。


image

這三個方法詳細(xì)的語義具體如下所示:

  • init():設(shè)置計數(shù)器的初始值。
  • down():計數(shù)器的值減1;如果此時計數(shù)器的值小于0,則當(dāng)前線程將被阻塞,否則當(dāng)前線程可以繼續(xù)執(zhí)行。
  • up():計數(shù)器的值加1;如果此時計數(shù)器的值小于或者等于0,則喚醒等待隊列中的一個線程,并將其從等待隊列中移除。

在Java SDK里面,信號量模型是由java.util.concurrent.Semaphore實(shí)現(xiàn)的,Semaphore這個類能夠保證這三個方法都是原子操作。

class Semaphore{
  // 計數(shù)器
  int count;
  // 等待隊列
  Queue queue;
  // 初始化操作
  Semaphore(int c){
    this.count=c;
  }
  // 
  void down(){
    this.count--;
    if(this.count<0){
      //將當(dāng)前線程插入等待隊列
      //阻塞當(dāng)前線程
    }
  }
  void up(){
    this.count++;
    if(this.count<=0) {
      //移除等待隊列中的某個線程T
      //喚醒線程T
    }
  }
}

down()、up()這兩個操作歷史上最早稱為P操作和V操作,所以信號量模型也被稱為PV原語。在Java SDK并發(fā)包里,down()和up()對應(yīng)的則是acquire()和release()。

信號量是如何保證互斥的?
  • 假設(shè)兩個線程T1和T2同時訪問addOne()方法,當(dāng)它們同時調(diào)用acquire()的時候,由于acquire()是一個原子操作,所以只能有一個線程(假設(shè)T1)把信號量里的計數(shù)器減為0,另外一個線程(T2)則是將計數(shù)器減為-1。對于線程T1,信號量里面的計數(shù)器的值是0,大于等于0,所以線程T1會繼續(xù)執(zhí)行;對于線程T2,信號量里面的計數(shù)器的值是-1,小于0,按照信號量模型里對down()操作的描述,線程T2將被阻塞。所以此時只有線程T1會進(jìn)入臨界區(qū)執(zhí)行count+=1;。
  • 當(dāng)線程T1執(zhí)行release()操作,也就是up()操作的時候,信號量里計數(shù)器的值是-1,加1之后的值是0,小于等于0,按照信號量模型里對up()操作的描述,此時等待隊列中的T2將會被喚醒。于是T2在T1執(zhí)行完臨界區(qū)代碼之后才獲得了進(jìn)入臨界區(qū)執(zhí)行的機(jī)會,從而保證了互斥性。
快速實(shí)現(xiàn)一個限流器

Semaphore除了能像lock一樣實(shí)現(xiàn)互斥鎖之外,還可以允許多個線程訪問同一個臨界區(qū)。(這個功能是lock不容易實(shí)現(xiàn)的)

eg:如連接池、對象池、線程池等等。數(shù)據(jù)庫連接池,在同一時刻,一定是允許多個線程同時使用連接池的,當(dāng)然,每個連接在被釋放前,是不允許其他線程使用的。

下面是一個常量池的一個實(shí)現(xiàn)代碼:

class ObjPool<T, R> {
  final List<T> pool;
  // 用信號量實(shí)現(xiàn)限流器
  final Semaphore sem;
  // 構(gòu)造函數(shù)
  ObjPool(int size, T t){
    pool = new Vector<T>(){};
    for(int i=0; i<size; i++){
      pool.add(t);
    }
    sem = new Semaphore(size);
  }
  // 利用對象池的對象,調(diào)用func
  R exec(Function<T,R> func) {
    T t = null;
    sem.acquire();
    try {
      t = pool.remove(0);
      return func.apply(t);
    } finally {
      pool.add(t);
      sem.release();
    }
  }
}
// 創(chuàng)建對象池
ObjPool<Long, String> pool = 
  new ObjPool<Long, String>(10, 2);
// 通過對象池獲取t,之后執(zhí)行  
pool.exec(t -> {
    System.out.println(t);
    return t.toString();
});

總結(jié)一下:Semaphore就像旋轉(zhuǎn)壽司店,共有10個座位,當(dāng)座位有空余時,等待的人就可以坐上去。如果有只有2個空位,來的是一家3口,那就只有等待。如果來的是一對情侶,就可以直接坐上去吃。當(dāng)然如果同時空出5個空位,那一家3口和一對情侶可以同時上去吃。CountDownLatch就像大型商場里面的臨時游樂場,每一場游樂的時間過后等待的人同時進(jìn)場玩,而一場中間會有不愛玩了的人隨時出來,但不能進(jìn)入,一旦所有進(jìn)入的人都出來了,新一批人就可以同時進(jìn)場

他們與非共享最大的區(qū)別就在于是否能多個線程同時獲取鎖。

這里有semaphore和countDownlatch的一篇文章。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Semaphore 是什么 Semaphore 信號量,是JDK的并發(fā)包中提供了一個非常有用的工具類。可以控制某個...
    餓了爸閱讀 570評論 0 0
  • semaphore semaphore(信號量)應(yīng)用在并發(fā)領(lǐng)域編程,使用信號量能夠控制多線程執(zhí)行的并發(fā)執(zhí)行數(shù)。信號...
    taj3991閱讀 994評論 0 0
  • 1.Semaphore 信號量: 與Lock不同的是,Semaphore 可以允許多個線程訪問一個臨界區(qū)。 1....
    流螢飄楓閱讀 535評論 0 0
  • 線程狀態(tài)新建,就緒,運(yùn)行,阻塞,死亡。 線程同步多線程可以同時運(yùn)行多個任務(wù),線程需要共享數(shù)據(jù)的時候,可能出現(xiàn)數(shù)據(jù)不...
    KevinCool閱讀 880評論 0 0
  • 1) 回鄉(xiāng)幾日,基本都是宅著。 不愿見更多的人,尤其是一些需要在臺面上應(yīng)酬的人。若有人流露出此意,便早早投降。 不...
    歐小麗閱讀 316評論 1 6

友情鏈接更多精彩內(nèi)容