Semaphore 原理簡介和使用

Semaphore共享鎖

簡介

`在多線程環境下用於協調各個線程, 以保證它們可以正確、合理的使用公共資源
信號量維護了一個許可集,咱們在初始化Semaphore時須要爲這個許可集傳入一個數量值,
該數量值表明同一時間能訪問共享資源的線程數量。
線程能夠經過acquire()方法獲取到一個許可,而後對共享資源進行操做,
若是許可集已分配完了,那麼線程將進入等待狀態,
直到其餘線程釋放許可纔有機會再獲取許可,線程釋放一個許可經過release()方法完成
`node

DEMO瞭解其用法

`上述示例說明: 安全

在建立Semaphore時初始化5個許可,這也就意味着同一個時間點容許5個線程進行共享資源訪問,
使用acquire()方法爲每一個線程獲取許可,並進行休眠1秒,若是5個許可已被分配完,
新到來的線程將進入等待狀態。若是線程順利完成操做將經過release()方法釋放許可,
咱們執行代碼,能夠發現每隔1秒幾乎同一時間出現5條線程訪問
`多線程

Semaphore實現互斥鎖

簡介

`在初始化信號量時傳入1,使得它在使用時最多隻有一個可用的許可,從而可用做一個相互排斥的鎖。
這一般也稱爲二進制信號量,由於它只能有兩種狀態:一個可用的許可或零個可用的許可。
按此方式使用時,二進制信號量具備某種屬性(與不少 Lock 實現不一樣),便可以由線程釋放「鎖」,而不是由全部者(由於信號量沒有全部權的概念) 併發

`源碼分析

DEMO瞭解其用法

`建立一個數量爲1的互斥信號量Semaphore,
而後併發執行10個線程,在線程中利用Semaphore控制線程的併發執行,
由於信號量數值只有1,所以每次只能一條線程執行,其餘線程進入等待狀態
`ui

Semaphore提供的方法

  • Semaphore(int permits) 建立具備給定的許可數和非公平的公平設置的Semaphore
  • Semaphore(int permits, boolean fair) 建立具備給定的許可數和給定的公平設置的Semaphore,true即爲公平鎖
  • void acquireUninterruptibly() 今後信號量中獲取許可,不可中斷
  • int availablePermits() 返回此信號量中當前可用的許可數
  • int drainPermits() 獲取並返回當即可用的全部許可
  • protected Collection<Thread> getQueuedThreads() 返回一個 collection,包含可能等待獲取的線程
  • int getQueueLength() 返回正在等待獲取的線程的估計數目
  • boolean hasQueuedThreads() 查詢是否有線程正在等待獲取
  • boolean isFair() 若是此信號量的公平設置爲 true,則返回 true
  • boolean tryAcquire() 僅在調用時此信號量存在一個可用許可,才從信號量獲取許可
  • boolean tryAcquire(long timeout, TimeUnit unit) 若是在給定的等待時間內,此信號量有可用的許可而且當前線程未被中斷,則今後信號量獲取一個許可

內部原理分析

類圖

`AQS是基礎組件,只負責核心併發操做,如加入或維護同步隊列,控制同步狀態,等,
而具體的加鎖和解鎖操做交由子類完成,
所以子類Semaphore共享鎖的獲取與釋放須要本身實現,
這兩個方法分別是獲取鎖的tryAcquireShared(int arg)方法和釋放鎖的tryReleaseShared(int arg)方法,這點從Semaphore的內部結構徹底能夠看出來
`spa

`Semaphore的內部類公平鎖(FairSync)和非公平鎖(NoFairSync)各自實現不一樣的獲取鎖方法即tryAcquireShared(int arg),
畢竟公平鎖和非公平鎖的獲取稍後不一樣,
而釋放鎖tryReleaseShared(int arg)的操做交由Sync實現,由於釋放操做都是相同的,所以放在父類Sync中實現固然是最好的
`線程

源碼分析

`默認非公平鎖
`3d

`初始化信號量的時候 傳入的 permits 參數 最終會賦值到aqs的state變量 
並對state進行cas操做 對象

調用Semaphore的acquire()方法後,
執行過程是這樣的,
當一個線程請求到來時,若是state值表明的許可數足夠使用,
那麼請求線程將會得到同步狀態即對共享資源的訪問權,並更新state的值(通常是對state值減1),
但若是state值表明的許可數已爲0,則請求線程將沒法獲取同步狀態,
線程將被加入到同步隊列並阻塞,
直到其餘線程釋放同步狀態(通常是對state值加1)纔可能獲取對共享資源的訪問權
`

`先獲取state的值,並執行減法操做,獲得remaining值,
若是remaining大於等於0,那麼線程獲取同步狀態成功,可訪問共享資源,並更新state的值,
若是remaining小於0,那麼線程獲取同步狀態失敗,將被加入同步隊列(經過doAcquireSharedInterruptibly(arg))

採用無鎖(CAS)併發的操做保證對state值修改的安全

`

`一、在方法中,因爲當前線程沒有獲取同步狀態,所以建立一個共享模式(Node.SHARED)的結點並經過addWaiter(Node.SHARED)加入同步隊列,

二、加入完成後,當前線程進入自旋狀態,首先判斷前驅結點是否爲head,

a、若是是,那麼嘗試獲取同步狀態並返回r值,若是r大於0,則說明獲取同步狀態成功,將當前線程設置爲head並傳播,傳播指的是,同步狀態剩餘的許可數值不爲0,通知後續結點繼續獲取同步狀態,到此方法將會return結束,獲取到同步狀態的線程將會執行原定的任務。

b、若是前驅結點不爲head或前驅結點爲head並嘗試獲取同步狀態失敗,那麼調用shouldParkAfterFailedAcquire(p, node)方法判斷前驅結點的waitStatus值是否爲SIGNAL並調整同步隊列中的node結點狀態,若是返回true,那麼執行parkAndCheckInterrupt()方法,將當前線程掛起並返回是否中斷線程的flag

`

` 在AQS中存在一個變量state,當咱們建立Semaphore對象傳入許可數值時, 最終會賦值給state,state的數值表明同一個時刻可同時操做共享數據的線程數量, 每當一個線程請求(如調用Semaphored的acquire()方法)獲取同步狀態成功, state的值將會減小1,直到state爲0時,表示已沒有可用的許可數, 也就是對共享數據進行操做的線程數已達到最大值,其餘後來線程將被阻塞, 此時AQS內部會將線程封裝成共享模式的Node結點,加入同步隊列中等待並開啓自旋操做。 只有當持有對共享數據訪問權限的線程執行完成任務並釋放同步狀態後, 同步隊列中的對於的結點線程纔有可能獲取同步狀態並被喚醒執行同步操做,注 意在同步隊列中獲取到同步狀態的結點將被設置成head並清空相關線程數據(畢竟線程已在執行也就沒有必要保存信息了), AQS經過這種方式便實現共享鎖 `

相關文章
相關標籤/搜索