Java并發(fā)之Semaphore工具類r的全面解析
內(nèi)容概要
Semaphore通過控制許可數(shù)量,實(shí)現(xiàn)了對(duì)并發(fā)線程數(shù)的精細(xì)管理,有效避免了資源競爭和過載問題,能顯著提升系統(tǒng)吞吐量和響應(yīng)速度,同時(shí),Semaphore還支持公平與非公平策略,具有更好的靈活性和適應(yīng)性,滿足了不同業(yè)務(wù)場(chǎng)景的需求。
核心概念
Semaphore
是 java.util.concurrent
中非常有用的并發(fā)編程工具類,它通常被用于限制對(duì)某個(gè)資源或資源池的并發(fā)訪問數(shù)量。舉個(gè)實(shí)際的例子:假設(shè)一個(gè)餐廳里只有 10 張桌子,在繁忙的用餐時(shí)段,很多顧客會(huì)同時(shí)來到餐廳,但餐廳的空間有限,不能同時(shí)容納所有顧客,這時(shí),就需要一種機(jī)制來控制進(jìn)入餐廳的顧客數(shù)量,確保餐廳不會(huì)過于擁擠。
Semaphore
就可以扮演這個(gè)控制者的角色,可以將 Semaphore
的許可數(shù)設(shè)置為 10,這代表著餐廳里最多可以有 10 組顧客同時(shí)用餐,每當(dāng)有顧客進(jìn)入餐廳并坐下時(shí),Semaphore
的許可數(shù)就會(huì)減 1;每當(dāng)有顧客用餐完畢離開時(shí),Semaphore
的許可數(shù)就會(huì)加 1,如果所有桌子都坐滿了,后來的顧客就需要在門外等待,直到有桌子空出來。
在這種業(yè)務(wù)場(chǎng)景下,使用Semaphore
就可以有效地控制餐廳的擁擠程度,保證了顧客的用餐體驗(yàn)。
具體來說,Semaphore
通常用于以下場(chǎng)景:
- 限制并發(fā)訪問量:當(dāng)一個(gè)系統(tǒng)或應(yīng)用需要限制對(duì)某個(gè)共享資源(如數(shù)據(jù)庫連接、文件、網(wǎng)絡(luò)服務(wù)等)的并發(fā)訪問量時(shí),
Semaphore
可以用來控制同時(shí)訪問這些資源的線程數(shù),通過設(shè)置Semaphore
的許可數(shù),可以確保不會(huì)有過多的線程同時(shí)訪問資源,從而防止資源過載或爭用條件導(dǎo)致的性能下降。 - 實(shí)現(xiàn)線程同步:除了限制并發(fā)訪問量外,
Semaphore
還可以用于協(xié)調(diào)多個(gè)線程的執(zhí)行順序,例如,在一個(gè)多線程程序中,如果某個(gè)操作需要多個(gè)線程按照特定的順序執(zhí)行,可以使用Semaphore
來控制這些線程的執(zhí)行流程。 - 資源池管理:
Semaphore
還可以用于管理資源池,例如連接池、線程池等,通過動(dòng)態(tài)調(diào)整Semaphore
的許可數(shù),可以根據(jù)系統(tǒng)的負(fù)載情況動(dòng)態(tài)地增加或減少資源的使用量,從而提高系統(tǒng)的伸縮性和資源利用率。
Semaphore
是一種靈活的同步工具,它非常適合在信號(hào)量場(chǎng)景中控制對(duì)資源的訪問,能夠在多線程環(huán)境中提供細(xì)粒度的控制,幫助開發(fā)者有效地管理系統(tǒng)資源,比如,用來控制對(duì)數(shù)據(jù)庫連接、線程池資源或其他共享資源的并發(fā)訪問,從而避免資源爭用和系統(tǒng)過載,保證系統(tǒng)的穩(wěn)定性和性能。
代碼案例
下面是一個(gè)簡單的Java代碼示例,演示了如何使用Semaphore
來限制對(duì)一組資源的并發(fā)訪問,如下代碼:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SemaphoreExample { // 創(chuàng)建一個(gè)Semaphore,初始許可為3,表示資源池中最多有3個(gè)資源可用 private static final Semaphore semaphore = new Semaphore(3); public static void main(String[] args) { // 創(chuàng)建一個(gè)固定大小的線程池來模擬客戶端請(qǐng)求 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交10個(gè)任務(wù)到線程池,每個(gè)任務(wù)代表一個(gè)客戶端請(qǐng)求 for (int i = 0; i < 10; i++) { executor.submit(() -> { try { // 線程嘗試獲取許可 semaphore.acquire(); System.out.println("線程" + Thread.currentThread().getName() + "獲取到資源,開始處理..."); // 模擬資源處理時(shí)間 Thread.sleep((long) (Math.random() * 1000)); System.out.println("線程" + Thread.currentThread().getName() + "處理完畢,釋放資源..."); // 線程釋放許可 semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 關(guān)閉線程池 executor.shutdown(); } }
在上面代碼中,創(chuàng)建了一個(gè)Semaphore
實(shí)例,初始許可設(shè)置為3,這意味著最多只能有3個(gè)線程同時(shí)訪問資源,然后創(chuàng)建了一個(gè)固定大小為5的線程池來模擬客戶端請(qǐng)求,提交了10個(gè)任務(wù)到線程池,每個(gè)任務(wù)都嘗試獲取Semaphore
的許可,模擬資源的訪問。
當(dāng)線程調(diào)用semaphore.acquire()
時(shí),它會(huì)嘗試獲取一個(gè)許可,如果許可可用,線程將繼續(xù)執(zhí)行;如果沒有許可可用,線程將被阻塞,直到有許可可用。當(dāng)線程完成資源訪問后,它調(diào)用semaphore.release()
來釋放許可,這樣其他等待的線程就可以獲取許可并訪問資源了。
如下輸出結(jié)果:
線程pool-1-thread-1獲取到資源,開始處理...
線程pool-1-thread-2獲取到資源,開始處理...
線程pool-1-thread-3獲取到資源,開始處理...
線程pool-1-thread-3處理完畢,釋放資源...
線程pool-1-thread-1處理完畢,釋放資源...
線程pool-1-thread-4獲取到資源,開始處理...
線程pool-1-thread-2處理完畢,釋放資源...
線程pool-1-thread-5獲取到資源,開始處理...
線程pool-1-thread-4處理完畢,釋放資源...
線程pool-1-thread-5處理完畢,釋放資源...
線程pool-1-thread-3獲取到資源,開始處理...
線程pool-1-thread-3處理完畢,釋放資源...
從輸出中可以看到,盡管有一個(gè)大小為5的線程池,但Semaphore
確保了同時(shí)訪問資源的線程數(shù)不超過3個(gè),當(dāng)線程處理完資源后,它會(huì)釋放許可,允許其他線程獲取許可并繼續(xù)執(zhí)行。
核心API
下面是 Semaphore
類中一些重要方法的解釋:
acquire()
此方法用于獲取一個(gè)許可,如果當(dāng)前沒有可用的許可,則當(dāng)前線程會(huì)被阻塞,直到有許可可用,這是 Semaphore
最基本的使用方式,用于控制對(duì)資源的訪問。
acquire(int permits)
這個(gè)方法允許一次性獲取多個(gè)許可,如果當(dāng)前可用許可數(shù)量少于請(qǐng)求的數(shù)量,則線程會(huì)被阻塞,直到有足夠的許可可用。
acquireUninterruptibly()
acquireUninterruptibly()
和acquireUninterruptibly(int permits)
這兩個(gè)方法與 acquire
類似,但區(qū)別在于它們不會(huì)響應(yīng)中斷,即使其他線程中斷了正在等待的線程,這些線程也會(huì)繼續(xù)等待,直到獲得許可。
tryAcquire
tryAcquire()
和tryAcquire(int permits)
這兩個(gè)方法嘗試獲取一個(gè)或多個(gè)許可,如果當(dāng)前有可用的許可則立即返回 true
,并且獲取成功;如果沒有可用許可則立即返回 false
,線程不會(huì)被阻塞。
tryAcquire(long timeout, TimeUnit unit)
tryAcquire(long timeout, TimeUnit unit)和tryAcquire(int permits, long timeout, TimeUnit unit)這兩個(gè)方法嘗試在給定的時(shí)間內(nèi)獲取一個(gè)或多個(gè)許可,如果在指定的時(shí)間內(nèi)獲得了許可,則返回 true
;否則,返回 false
,如果線程在等待期間被中斷,那么這兩個(gè)方法都會(huì)拋出 InterruptedException
。
release
release()此方法用于釋放一個(gè)許可,將其返回給 Semaphore
,這樣其他等待的線程就可以獲取它,通常在完成對(duì)資源的訪問后調(diào)用此方法。
release(int permits)
release(int permits)這個(gè)方法允許一次性釋放多個(gè)許可。
availablePermits
availablePermits()這個(gè)方法返回當(dāng)前 Semaphore
中可用的許可數(shù)量。
hasQueuedThreads()
檢查是否有任何線程正在等待獲取許可。
getQueueLength()
返回正在等待獲取許可的線程數(shù)。
drainPermits()
此方法返回并刪除當(dāng)前所有可用的許可,主要用于一些特殊的場(chǎng)景,比如需要在某個(gè)時(shí)刻一次性消耗所有許可。
reducePermits(int reduction)
這個(gè)方法用于減少 Semaphore
中的可用許可數(shù)量,通常用于一些動(dòng)態(tài)調(diào)整資源池大小的場(chǎng)景,需要注意的是,這個(gè)方法不會(huì)阻塞,如果減少后的許可數(shù)小于0,那么會(huì)拋出 IllegalArgumentException
。
注意:Semaphore
并不保證獲取許可的公平性,即等待時(shí)間最長的線程不一定會(huì)優(yōu)先獲得許可。
核心總結(jié)
優(yōu)點(diǎn)
- 簡單易用:通過許可數(shù)量,輕松控制并發(fā)線程數(shù)。
- 高效:避免了不必要的線程創(chuàng)建和銷毀,提高了系統(tǒng)吞吐量。
- 靈活:支持公平和非公平策略,可根據(jù)需求選擇。
缺點(diǎn)
- 不保證公平性:默認(rèn)策略下,先到的線程不一定先獲得許可。
- 可能導(dǎo)致死鎖:使用不當(dāng)(如在持有Semaphore時(shí)執(zhí)行其他阻塞操作)時(shí),可能會(huì)引發(fā)死鎖。
以上就是Java并發(fā)之Semaphore工具類r的全面解析的詳細(xì)內(nèi)容,更多關(guān)于Java Semaphore的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java和MySQL數(shù)據(jù)庫中關(guān)于小數(shù)的保存問題詳析
在Java和MySQL中小數(shù)的精度可能會(huì)受到限制,如float類型的小數(shù)只能精確到6-7位,double類型也只能精確到15-16位,這篇文章主要給大家介紹了關(guān)于Java和MySQL數(shù)據(jù)庫中關(guān)于小數(shù)的保存問題,需要的朋友可以參考下2024-01-01一文帶你學(xué)會(huì)規(guī)則引擎Drools的應(yīng)用
Drools?就是一個(gè)開源的業(yè)務(wù)規(guī)則引擎,可以很容易地與?spring?boot?應(yīng)用程序集成,這篇文章就來和大家詳細(xì)聊聊Drools的具體應(yīng)用,需要的可以參考一下2023-03-03用SpringBoot Admin監(jiān)控SpringBoot程序
這篇文章主要介紹了用SpringBoot Admin監(jiān)控SpringBoot程序,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10對(duì)Java字符串與整形、浮點(diǎn)類型之間的相互轉(zhuǎn)換方法總結(jié)
今天小編就為大家分享一篇對(duì)Java字符串與整形、浮點(diǎn)類型之間的相互轉(zhuǎn)換方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07淺談Arrays.asList() 和ArrayList類型區(qū)別
下面小編就為大家?guī)硪黄狝rrays.asList() 和ArrayList類型區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10java: 程序包c(diǎn)om.fasterxml.jackson.annotation不存在的解決辦法
當(dāng)我們?cè)趯?dǎo)入程序之后,系統(tǒng)給出錯(cuò)誤提示:java: 程序包c(diǎn)om.fasterxml.jackson.annotation不存在,本文主要介紹了Java程序包不存在的三種解決方法,需要的朋友可以參考下2024-02-02Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧2024-08-08SpringBoot的HTTPS配置實(shí)現(xiàn)
本文主要介紹了SpringBoot的HTTPS配置實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04