Java并發(fā)之Semaphore工具類r的全面解析
內(nèi)容概要
Semaphore通過控制許可數(shù)量,實(shí)現(xiàn)了對(duì)并發(fā)線程數(shù)的精細(xì)管理,有效避免了資源競(jìng)爭(zhēng)和過載問題,能顯著提升系統(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ù)庫(kù)連接、文件、網(wǎng)絡(luò)服務(wù)等)的并發(fā)訪問量時(shí),
Semaphore可以用來控制同時(shí)訪問這些資源的線程數(shù),通過設(shè)置Semaphore的許可數(shù),可以確保不會(huì)有過多的線程同時(shí)訪問資源,從而防止資源過載或爭(zhēng)用條件導(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ù)庫(kù)連接、線程池資源或其他共享資源的并發(fā)訪問,從而避免資源爭(zhēng)用和系統(tǒng)過載,保證系統(tǒng)的穩(wěn)定性和性能。
代碼案例
下面是一個(gè)簡(jiǎn)單的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í)間最長(zhǎng)的線程不一定會(huì)優(yōu)先獲得許可。
核心總結(jié)
優(yōu)點(diǎn)
- 簡(jiǎ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 Stack詳細(xì)介紹和示例學(xué)習(xí)
JAVA Stack是棧。它的特性是:先進(jìn)后出(FILO, First In Last Out)。2013-11-11
Java NIO.2 使用Path接口來監(jiān)聽文件、文件夾變化
Java7對(duì)NIO進(jìn)行了大的改進(jìn),新增了許多功能,接下來通過本文給大家介紹Java NIO.2 使用Path接口來監(jiān)聽文件、文件夾變化 ,需要的朋友可以參考下2019-05-05
MyEclipse開發(fā)一個(gè)webservice接口
這篇文章主要為大家詳細(xì)介紹了MyEclipse開發(fā)一個(gè)webservice接口,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
spring boot如何實(shí)現(xiàn)切割分片上傳
這篇文章主要介紹了spring boot如何實(shí)現(xiàn)切割分片上傳,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
java ThreadPoolExecutor 并發(fā)調(diào)用實(shí)例詳解
這篇文章主要介紹了java ThreadPoolExecutor 并發(fā)調(diào)用實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
SpringBoot實(shí)現(xiàn)熱部署的三種方式
本文主要介紹了SpringBoot實(shí)現(xiàn)熱部署的三種方式,主要包括配置pom.xml文件,使用插件的執(zhí)行命令mvn spring-boot:run啟動(dòng)項(xiàng),使用springloader本地啟動(dòng)修改jvm參數(shù),使用devtools工具包,感興趣的可以了解一下2023-12-12
java接收文件流+response.body()調(diào)用兩次問題(分別接收文件和對(duì)象)
這篇文章主要介紹了java接收文件流+response.body()調(diào)用兩次問題(分別接收文件和對(duì)象),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06

