多線程并發(fā)控制工具Semaphore的使用詳解
當(dāng)在多線程運(yùn)行的場(chǎng)景,部分共享資源會(huì)存在資源的沖突和競(jìng)爭(zhēng),為了改善資源使用的方式,是否可以通過控制某個(gè)方法允許并發(fā)訪問線程的數(shù)量?
如下圖所示:
Semaphore可以有效的緩解這個(gè)問題。
1、Semaphore類
在jdk中提供了一個(gè)Semaphore類(信號(hào)量)
它提供了兩個(gè)方法:
- semaphore.acquire() 請(qǐng)求信號(hào)量,可以限制線程的個(gè)數(shù),是一個(gè)正數(shù),如果信號(hào)量是-1,就代表已經(jīng)用完了信號(hào)量,其他線程需要阻塞了。
- 第二個(gè)方法是semaphore.release(),代表是釋放一個(gè)信號(hào)量,此時(shí)信號(hào)量的個(gè)數(shù)+1。
2、基本概念
2.1、信號(hào)量
Semaphore
維護(hù)了一個(gè)計(jì)數(shù)器(許可的數(shù)量),表示可以同時(shí)訪問某個(gè)資源的線程數(shù)量。線程通過申請(qǐng)?jiān)S可來訪問資源。
2.2、計(jì)數(shù)器
信號(hào)量的計(jì)數(shù)器可以被設(shè)置為一個(gè)初始值,該值表示許可的數(shù)量。每當(dāng)一個(gè)線程獲取許可時(shí),計(jì)數(shù)器減一;當(dāng)釋放許可時(shí),計(jì)數(shù)器加一。
2.3、公平性
Semaphore
可以配置為公平或非公平。公平的信號(hào)量遵循 FIFO(先入先出)原則,非公平信號(hào)量則不保證獲取的順序。
代碼示例:
import java.util.concurrent.Semaphore; public class SemaphoreTest { public static void main(String[] args) { final DatabaseConnectionPool pool = new DatabaseConnectionPool(3); // 創(chuàng)建多個(gè)線程以模擬數(shù)據(jù)庫連接 Thread thread1 = new Thread(() -> pool.connect("Thread 1")); Thread thread2 = new Thread(() -> pool.connect("Thread 2")); Thread thread3 = new Thread(() -> pool.connect("Thread 3")); Thread thread4 = new Thread(() -> pool.connect("Thread 4")); Thread thread5 = new Thread(() -> pool.connect("Thread 5")); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread5.start(); } } class DatabaseConnectionPool{ private final Semaphore semaphore; DatabaseConnectionPool(int maxConnections) { this.semaphore = new Semaphore(maxConnections,true); } public void connect(String threadName) { try { System.out.println(threadName + " is trying to connect."); // 獲取許可 semaphore.acquire(); System.out.println(threadName + " has connected to the database."); // 模擬使用連接 Thread.sleep(5000); // 模擬數(shù)據(jù)庫操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 釋放許可 System.out.println(threadName + " is releasing the connection."); semaphore.release(); } } }
代碼解析
1.Semaphore 的創(chuàng)建:
public DatabaseConnectionPool(int maxConnections) { this.semaphore = new Semaphore(maxConnections); }
通過指定最大連接數(shù)來初始化信號(hào)量。
2.獲取連接
semaphore.acquire();
線程嘗試獲取信號(hào)量的許可,如果沒有可用的許可,則該線程會(huì)被阻塞,直到有許可可用。
3.釋放連接
semaphore.release();
訪問完成后,線程釋放許可,讓其他線程能夠訪問。
多線程模擬:
使用多個(gè)線程來模擬多個(gè)連接請(qǐng)求,只有 3 個(gè)線程能同時(shí)獲取許可。
3、使用場(chǎng)景
- 控制并發(fā)訪問某些資源,例如數(shù)據(jù)庫連接、文件句柄等。
- 限制同時(shí)執(zhí)行的線程數(shù)量,以避免系統(tǒng)負(fù)載過大。
4、死鎖
死鎖是一種情況,其中兩個(gè)或多個(gè)線程永遠(yuǎn)互相等待對(duì)方釋放資源,從而導(dǎo)致程序無法繼續(xù)執(zhí)行。
了解更多死鎖知識(shí),可參考:有關(guān)Java死鎖和活鎖的聯(lián)系
4.1、條件
- 互斥條件:至少有一個(gè)資源處于非共享模式,即某一時(shí)刻只能被一個(gè)線程使用。
- 保持并等待條件:一個(gè)線程保持至少一個(gè)資源并等待其他被其他線程占用的資源。
- 不剝奪條件:資源不能被強(qiáng)行奪走,只能由持有該資源的線程釋放。
- 循環(huán)等待條件:存在一個(gè)線程的集合,使得每個(gè)線程都在等待下一個(gè)線程持有的資源。
4.2、解決策略
- 資源順序申請(qǐng):確保所有線程按照相同的順序請(qǐng)求多個(gè)資源,這樣可以避免循環(huán)等待。
- 設(shè)置超時(shí):在請(qǐng)求資源時(shí)設(shè)置超時(shí),如果請(qǐng)求在一定時(shí)間內(nèi)沒有成功,線程應(yīng)該釋放它已持有的資源,有可能中斷互斥條件。
- 使用
tryLock
和tryAcquire
:可使用Lock
和Semaphore
的嘗試獲取方法,在未能成功獲取時(shí),可以做適當(dāng)?shù)腻e(cuò)誤處理或重試,而不是靜默等待。 - 避免持有狀態(tài):盡量避免在一個(gè)線程中持有多個(gè)鎖,或者隔離資源,以減少死鎖風(fēng)險(xiǎn)。
4.3、聯(lián)系
雖然 Semaphore
可以在某種情況下幫助減少發(fā)生死鎖的機(jī)會(huì),但它并不是解決死鎖問題的直接手段。
Semaphore
控制訪問的方式可以導(dǎo)致某些設(shè)計(jì)上的改善,例如:
- 限制資源的同時(shí)訪問:
Semaphore
可用于限制可同時(shí)訪問某種資源的線程數(shù)量,從而減少復(fù)雜的資源使用模式。 - 避免持有過多的鎖:通過合理設(shè)計(jì)線程的資源申請(qǐng)和釋放邏輯,結(jié)合
Semaphore
,可以減少因線程在持有多個(gè)資源時(shí)發(fā)生互斥和等待的可能性。
總結(jié)
Semaphore 是一個(gè)非常有用的并發(fā)控制工具,可以有效地控制對(duì)共享資源的訪問。通過合理使用它,可以避免過多線程同時(shí)訪問相同資源造成的競(jìng)爭(zhēng)和沖突,從而提高并發(fā)程序的安全性和效率。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot 使用WebSocket功能(實(shí)現(xiàn)步驟)
本文通過詳細(xì)步驟介紹了SpringBoot 使用WebSocket功能,首先需要導(dǎo)入WebSocket坐標(biāo),編寫WebSocket配置類,用于注冊(cè)WebSocket的Bean,結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02idea中增強(qiáng)for循環(huán)提示unexpected token問題
這篇文章主要介紹了idea中增強(qiáng)for循環(huán)提示unexpected token問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Spring之spring-context-indexer依賴詳解
這篇文章主要介紹了Spring之spring-context-indexer依賴詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11java實(shí)現(xiàn)字符串轉(zhuǎn)String數(shù)組的方法示例
這篇文章主要介紹了java實(shí)現(xiàn)字符串轉(zhuǎn)String數(shù)組的方法,涉及java字符串的遍歷、分割、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Java實(shí)現(xiàn)學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)學(xué)生管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01