在多線程編程中,控制并發訪問共享資源是一個常見的問題。Java提供了多種同步工具來幫助開發者解決這些問題,其中Semaphore
是一個非常重要的同步組件。Semaphore
基于AQS(AbstractQueuedSynchronizer)實現,可以用來控制同時訪問特定資源的線程數量。本文將詳細介紹Semaphore
的使用方法、底層實現、高級用法以及注意事項,幫助讀者更好地理解和應用Semaphore
。
Semaphore
(信號量)是一種用于控制多個線程對共享資源訪問的同步工具。它可以用來限制同時訪問某一資源的線程數量。Semaphore
維護了一個許可集,線程在訪問資源之前必須先獲取許可,訪問結束后釋放許可。如果許可集為空,則線程必須等待,直到有其他線程釋放許可。
Semaphore
常用于以下場景:
Semaphore
的構造函數有兩個主要參數:
permits
:許可的數量,即允許同時訪問資源的線程數量。fair
:是否公平,如果為true
,則等待時間最長的線程優先獲取許可。Semaphore semaphore = new Semaphore(10, true); // 創建一個有10個許可的公平信號量
線程在訪問資源之前需要調用acquire()
方法獲取許可。如果許可集為空,則線程進入阻塞狀態,直到有許可可用。
semaphore.acquire(); // 獲取一個許可
線程在訪問資源結束后需要調用release()
方法釋放許可,以便其他線程可以獲取許可。
semaphore.release(); // 釋放一個許可
以下是一個簡單的示例,展示了如何使用Semaphore
來控制對共享資源的訪問。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final int THREAD_COUNT = 10;
private static final Semaphore semaphore = new Semaphore(5); // 允許5個線程同時訪問
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new Worker(i)).start();
}
}
static class Worker implements Runnable {
private final int id;
Worker(int id) {
this.id = id;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(2000); // 模擬工作
System.out.println("Thread " + id + " has finished");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
}
AQS(AbstractQueuedSynchronizer)是Java并發包中的一個核心類,用于構建鎖和其他同步組件。AQS通過一個FIFO隊列來管理等待線程,并提供了獨占模式和共享模式兩種同步方式。
Semaphore
是基于AQS的共享模式實現的。AQS的state
變量表示當前可用的許可數量。Semaphore
通過調用AQS的acquireShared()
和releaseShared()
方法來實現許可的獲取和釋放。
以下是Semaphore
的部分源碼分析:
public class Semaphore implements java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining)) {
return remaining;
}
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) {
return true;
}
}
}
}
static final class NonfairSync extends Sync {
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
static final class FairSync extends Sync {
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors()) {
return -1;
}
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining)) {
return remaining;
}
}
}
}
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
}
Semaphore
支持公平性和非公平性兩種模式。在公平模式下,等待時間最長的線程優先獲取許可;在非公平模式下,線程獲取許可的順序不確定。
Semaphore fairSemaphore = new Semaphore(10, true); // 公平信號量
Semaphore nonFairSemaphore = new Semaphore(10, false); // 非公平信號量
acquire()
方法會阻塞線程直到獲取許可,但線程在等待過程中可能會被中斷。acquireUninterruptibly()
方法則不會響應中斷。
semaphore.acquireUninterruptibly(); // 不可中斷的獲取許可
tryAcquire(long timeout, TimeUnit unit)
方法允許線程在指定的時間內嘗試獲取許可,如果超時則返回false
。
boolean acquired = semaphore.tryAcquire(1, TimeUnit.SECONDS); // 嘗試在1秒內獲取許可
tryAcquire()
方法嘗試獲取許可,如果許可可用則立即返回true
,否則返回false
。
boolean acquired = semaphore.tryAcquire(); // 嘗試獲取許可
在使用Semaphore
時,如果多個線程相互等待對方釋放許可,可能會導致死鎖。為了避免死鎖,應確保線程獲取和釋放許可的順序一致。
如果線程獲取許可后沒有釋放許可,可能會導致資源泄漏。因此,務必在finally
塊中釋放許可。
try {
semaphore.acquire();
// 訪問共享資源
} finally {
semaphore.release();
}
在高并發場景下,Semaphore
的性能可能會成為瓶頸??梢酝ㄟ^調整許可數量、使用非公平模式或優化代碼邏輯來提高性能。
CountDownLatch
用于等待一組線程完成某個任務,而Semaphore
用于控制對共享資源的訪問。CountDownLatch
是一次性的,而Semaphore
可以重復使用。
CyclicBarrier
用于等待一組線程到達某個屏障點,然后同時繼續執行。Semaphore
則用于控制對共享資源的訪問。
ReentrantLock
是一種獨占鎖,同一時間只能有一個線程持有鎖。Semaphore
則允許多個線程同時訪問共享資源。
Semaphore
是Java并發編程中一個非常重要的同步組件,基于AQS實現,用于控制對共享資源的訪問。通過合理使用Semaphore
,可以有效地管理多線程環境下的資源競爭問題。本文詳細介紹了Semaphore
的基本使用、底層實現、高級用法以及注意事項,并與其他同步組件進行了比較。希望本文能幫助讀者更好地理解和應用Semaphore
。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。