【并發技術14】線程同步工具Semaphore的使用

      網友投稿 847 2025-03-31

      Semaphore 通常用于限制可以訪問某些資源(物理或邏輯的)線程數目,我們可以自己設定最大訪問量。它有兩個很常用的方法是?acquire()?和?release(),分別是獲得許可和釋放許可。

      官方JDK上面對 Semaphore 的解釋是這樣子的:

      Semaphore 通常用于限制可以訪問某些資源(物理或邏輯的)線程數目,我們可以自己設定最大訪問量。它有兩個很常用的方法是?acquire()?和?release(),分別是獲得許可和釋放許可。

      官方JDK上面對 Semaphore 的解釋是這樣子的:

      一個計數信號量。從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個?acquire(),然后再獲取該許可。每個?release()?添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,并采取相應的行動。拿到信號量的線程可以進入代碼,否則就等待。通過?acquire()?和?release()?獲取和釋放訪問許可。

      【并發技術14】線程同步工具Semaphore的使用

      我的解釋是這樣子的:

      Semaphore 相當于一個廁所,我在造的時候可以想造幾個坑就造幾個坑,假如現在我就造了3個坑,現在有10個人想要來上廁所,那么每次就只能3個人上,誰最先搶到誰就進去,出來了一個人后,第4個人才能進去,這個就限制了上廁所的人數了,就這個道理。每個人上廁所之前都先?acquire()?一下,如果有坑,就可以進入,沒有就被阻塞,在外面等;上完廁所后,會?release()?一下,釋放一個坑出來,以保證下一個人?acquire()?的時候有坑。

      我覺得我的解釋比官方的要好。

      1. Semaphore基本使用

      這Semaphore 在限制資源訪問量的問題上用處很大,比如限制一個文件的并發訪問次數等,它的原理很好理解。下面寫一個 Semphore 的示例代碼:

      public?class?SemaphoreTest?{????public?static?void?main(String[]?args)?{????????????ExecutorService?service?=?Executors.newCachedThreadPool();//使用并發庫,創建緩存的線程池????????final?Semaphore?sp?=?new?Semaphore(3);//創建一個Semaphore信號量,并設置最大并發數為3????????//availablePermits()?//用來獲取當前可用的訪問次數????????System.out.println("初始化:當前有"?+?(3?-?sp.availablePermits()?+?"個并發"));????????//創建10個任務,上面的緩存線程池就會創建10個對應的線程去執行????????for?(int?index?=?0;?index?

      代碼結構很容易理解,10個任務,每次最多3個線程去執行任務,其他線程被阻塞。可以通過打印信息來看線程的執行情況:

      初始化:當前有0個并發

      pool-1-thread-1獲取許可(0),剩余:1

      pool-1-thread-3獲取許可(2),剩余:0

      pool-1-thread-2獲取許可(1),剩余:1

      pool-1-thread-1釋放許可(0),剩余:3

      pool-1-thread-4獲取許可(3),剩余:1

      pool-1-thread-5獲取許可(4),剩余:1

      pool-1-thread-2釋放許可(1),剩余:3

      pool-1-thread-3釋放許可(2),剩余:3

      pool-1-thread-6獲取許可(5),剩余:0

      pool-1-thread-4釋放許可(3),剩余:2

      pool-1-thread-9獲取許可(8),剩余:0

      pool-1-thread-5釋放許可(4),剩余:2

      pool-1-thread-6釋放許可(5),剩余:2

      pool-1-thread-8獲取許可(7),剩余:0

      pool-1-thread-7獲取許可(6),剩余:2

      pool-1-thread-8釋放許可(7),剩余:2

      pool-1-thread-10獲取許可(9),剩余:2

      pool-1-thread-7釋放許可(6),剩余:2

      pool-1-thread-9釋放許可(8),剩余:2

      pool-1-thread-10釋放許可(9),剩余:3

      從結果中看,前三個為什么剩余的不是3,2,1呢?包括下面,每次釋放的時候剩余的量好像也不對,其實是對的,只不過線程運行太快,前三個是這樣子的:因為最大訪問量是3,所以前三個在打印語句之前都執行完了aquire()方法了,或者有部分執行了,從上面的結果來看,線程1是第一個進去的,線程2第二個進去,然后線程1和2開始打印,所以只剩1個了,接下來線程3進來了,打印只剩0個了。后面釋放的時候也是,打印前可能有不止一個釋放了。

      2. Semaphore同步問題

      我從網上查了一下,有些人說 Semaphore 實現了同步功能,我覺得不對,因為我自己寫了個測試代碼試了,并不會自己解決并發問題,如果多個線程操作同一個數據,還是需要自己同步一下的。然后我查了一下官方 JDK 文檔(要永遠相信官方的文檔),它里面是這樣說的:

      獲得一項前,每個線程必須從信號量獲取許可,從而保證可以使用該項。該線程結束后,將項返回到池中并將許可返回到該信號量,從而允許其他線程獲取該項。注意,調用?acquire()?時無法保持同步鎖,因為這會阻止將項返回到池中。信號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。

      這這段官方的解釋就很明確了,然后我就明白了網上有些人說的實現了同步的意思是信號量本身封裝所需的同步,也就是說我拿到了一個,別人就無法拿到了,我釋放了別人才能拿到(就跟我舉的廁所的坑一樣),但是我拿到了之后去操作公共數據的時候,針對這個數據操作的同步 Semaphore 就不管了,這就需要我們自己去同步了。下面寫一個同步的測試代碼:

      public?class?SemaphoreTest2?{????private?static?int?data?=?0;????public?static?void?main(String[]?args)?{????????ExecutorService?service?=?Executors.newCachedThreadPool();????????final?Semaphore?sp?=?new?Semaphore(3);????????System.out.println("初始化:當前有"?+?(3?-?sp.availablePermits()?+?"個并發"));????????//?10個任務????????for?(int?index?=?0;?index?

      看一下運行結果(部分)

      初始化:當前有0個并發

      pool-1-thread-2獲取許可(1),剩余:0

      pool-1-thread-2執行data自增前:data=0

      pool-1-thread-3獲取許可(2),剩余:0

      pool-1-thread-1獲取許可(0),剩余:0

      pool-1-thread-2執行data自增后:data=1

      pool-1-thread-3執行data自增前:data=1

      pool-1-thread-3執行data自增后:data=2

      pool-1-thread-1執行data自增前:data=2

      pool-1-thread-7獲取許可(6),剩余:1

      pool-1-thread-3釋放許可(2),剩余:2

      pool-1-thread-1執行data自增后:data=3

      從結果可以看出,每個線程在操作數據的前后,是不會受其他線程的影響的,但是其他線程可以獲取許可,獲取許可了之后就被阻塞在外面,等待當前線程操作完 data 才能去操作。當然也可以在當前線程操作 data 的時候,其他線程釋放許可,因為這完全不沖突。那如果把上面同步代碼塊去掉,再試試看會成什么亂七八糟的結果(部分):

      初始化:當前有0個并發

      pool-1-thread-3獲取許可(2),剩余:0

      pool-1-thread-2獲取許可(1),剩余:0

      pool-1-thread-3執行data自增前:data=0

      pool-1-thread-2執行data自增前:data=0

      pool-1-thread-1獲取許可(0),剩余:0

      pool-1-thread-3執行data自增后:data=1

      pool-1-thread-2執行data自增后:data=2

      pool-1-thread-7獲取許可(6),剩余:0

      pool-1-thread-1執行data自增前:data=2

      pool-1-thread-8獲取許可(7),剩余:0

      pool-1-thread-7執行data自增前:data=2

      pool-1-thread-2釋放許可(1),剩余:1

      pool-1-thread-7執行data自增后:data=4

      從結果中看,已經很明顯了,線程2和3都進去了,然后初始 data 都是0,線程3自增了一下,打印出1是沒問題的,但是線程2呢?也自增了一下,卻打印出了2。也就是說,線程2在操作數據的前后,數據已經被線程3修改過了,再一次證明 Semaphere 并沒有實現對共有數據的同步,在操作公共數據的時候,需要我們自己實現。

      Semaphere 中如果設置信號量為1的話,那就說明每次只能一個線程去操作任務,那這樣的話也就不存在線程安全問題了,所以如果設置信號量為1的話,就可以去掉那個 synchronized,不過效率就不行了。

      來源:微信公眾號

      任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:無代碼開發平臺的優勢(低代碼開發平臺優缺點)
      下一篇:excel分類(excel分類統計各出版社的教材數量)
      相關文章
      亚洲成色www久久网站夜月| 婷婷亚洲综合五月天小说在线| 亚洲精品国产日韩| 亚洲综合久久综合激情久久| 国产成人亚洲精品青草天美 | 亚洲一区精品伊人久久伊人| 亚洲欧美不卡高清在线| 亚洲日韩av无码中文| 亚洲中文无码卡通动漫野外| 久久久久se色偷偷亚洲精品av| 亚洲午夜电影在线观看| 亚洲国产午夜精品理论片| 亚洲国产精品免费在线观看| 亚洲噜噜噜噜噜影院在线播放| 91亚洲自偷在线观看国产馆| 亚洲a视频在线观看| 亚洲首页国产精品丝袜| 亚洲人成电影网站色| 亚洲精品无码人妻无码 | 亚洲国产精品不卡在线电影| 亚洲福利视频导航| 久久久无码精品亚洲日韩京东传媒| 久久水蜜桃亚洲av无码精品麻豆 | 亚洲国产精品成人网址天堂| 亚洲国产主播精品极品网红| 精品亚洲成α人无码成α在线观看 | 亚洲精品美女久久久久| 亚洲国产精品成人久久久| 2019亚洲午夜无码天堂| 亚洲欧美自偷自拍另类视| 亚洲成a∨人片在无码2023| 国产成人亚洲精品蜜芽影院| 亚洲中文字幕伊人久久无码| 亚洲精品白浆高清久久久久久| 久久久久亚洲Av片无码v| 亚洲欧洲校园自拍都市| 亚洲成年网站在线观看| 国产精品亚洲综合网站| 国产亚洲精品AA片在线观看不加载| 国产AV无码专区亚洲A∨毛片| 亚洲天天做日日做天天看 |