天天看點

一文講清Java中的信号量semaphore到底幹嘛的

信号量(英語:semaphore)又稱為信号标,是一個同步對象,用于保持在0至指定最大值之間的一個計數值。當線程完成一次對該semaphore對象的等待(wait)時,該計數值減一;當線程完成一次對semaphore對象的釋放(release)時,計數值加一。當計數值為0,則線程等待該semaphore對象不再能成功直至該semaphore對象變成signaled狀态。semaphore對象的計數值大于0,為signaled狀态;計數值等于0,為nonsignaled狀态.

semaphore對象适用于控制一個僅支援有限個使用者的共享資源,是一種不需要使用忙碌等待(busy waiting)的方法。

信号量的概念是由荷蘭計算機科學家艾茲赫爾·戴克斯特拉(Edsger W. Dijkstra)發明的,廣泛的應用于不同的作業系統中。在系統中,給予每一個程序一個信号量,代表每個程序目前的狀态,未得到控制權的程序會在特定地方被強迫停下來,等待可以繼續進行的信号到來。如果信号量是一個任意的整數,通常被稱為計數信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量隻有二進制的0或1,稱為二進制信号量(binary semaphore)。在linux系統中,二進制信号量(binary semaphore)又稱互斥鎖(Mutex)。

計數信号量具備兩種操作動作,稱為V(signal())與P(wait())(即部分參考書常稱的“PV操作”)。V操作會增加信号标S的數值,P操作會減少它。

運作方式:

初始化,給與它一個非負數的整數值。

運作P(wait()),信号标S的值将被減少。企圖進入臨界區段的程序,需要先運作P(wait())。當信号标S減為負值時,程序會被擋住,不能繼續;當信号标S不為負值時,程序可以獲準進入臨界區段。

運作V(signal()),信号标S的值會被增加。結束離開臨界區段的程序,将會運作V(signal())。當信号标S不為負值時,先前被擋住的其他程序,将可獲準進入臨界區段。

Windows API提供的semaphore

線程使用CreateSemaphore或CreateSemaphoreEx函數建立一個semaphore對象[1]。此時可以指定semaphore的目前計數值與計數值上限;也可指定semaphore對象的名字。其他程序中的線程可以指出已存在的semaphore對象的名字通過調用OpenSemaphore函數打開它。

如果多個線程在等待一個semaphore對象,不保證按照先進先出(FIFO)順序排程這些等待線程。外部事件,如核心模式的異步過程調用可改變等待順序。

在semaphore對象為signaled狀态時,等待函數傳回會把該semaphore對象計數值減1。函數ReleaseSemaphore把semaphore對象的計數值增加指定的值。任何線程,哪怕它沒有等待完成過該semaphore對象,也可以使用ReleaseSemaphore來增加semaphore對象的計數。如果ReleaseSemaphore導緻對象計數值超過上限,則該函數調用失敗,傳回298号錯誤:“Too many posts were made to a semaphore”。

一個線程多次等待同一個semaphore對象,每次等待操作完成都會降低semaphore對象計數值(直至計數值為0時該線程阻塞)。然而,通過multiple-object等待函數使用一個數組包含着同一個semaphore對象的多個句柄,不能實作對這個semaphore對象計數值的多次下降。

用完semaphore對象後,調用CloseHandle函數關閉它。semaphore對象的最後一個句柄被關閉後,作業系統會摧毀它。關閉semaphore并不影響它的計數值。是以,關閉semaphore前或者程序終止前,要確定已經正确調用過ReleaseSemaphore。否則,挂起等待該semaphore對象的線程會永久阻塞或逾時傳回。