一、概述
Semaphore信號量,主要功能是限制能夠運行的線程量,指定固定數量的許可,只有獲取許可的線程可以運行,若沒有許可獲取則阻塞,直到獲取許可。
二、源碼分析
1、構造函數
/**
* Creates a {@code Semaphore} with the given number of
* permits and nonfair fairness setting.
* 創建給定許可證數量信號量和非公平設置
* @param permits the initial number of permits available.
* This value may be negative, in which case releases
* must occur before any acquires will be granted.
* 初始許可證數量.
* 這個值允許爲負數,在這種情況下必須在任何獲取之前釋放
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
* @param fair {@code true} 保證競爭先進先出
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
Semaphore中同步器Sync繼承AQS實現AQS中的共享模式tryReleaseShared(int releases)方法,同步器只實現了共享鎖的釋放,並未實現鎖的獲取tryAcquireShared(int arg)方法;
Semaphore中公平和非公平是通過它的兩個私有的靜態內部類FairSync、NonFairSync實現,他們通過繼承Sync並實現共享鎖的獲取tryAcquireShared(int arg)方法;
非公平實現:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
// 只是實現了AQS中的共享鎖的獲取
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
公平實現:
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
// 只是實現了AQS中的共享鎖的獲取
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;
}
}
}
2、void acquire()
這個方法是從信號量獲取一個許可,在獲取到許可,或線程中斷之前,當前線程阻塞;獲取許可後立即返回並將許可數減一
/**
* 如果沒有許可可用,則會休眠知道發生以下兩種情況
* 1、其他調用release方法釋放許可,並且當前線程獲取到許可
* 2、其他線程中斷了當前線程
* 1)當前線程在進入這個方法時設置了中斷標誌位
* 2)等待許可時發生了中斷,則拋出中斷異常
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
這個方法是直接調用AQS的acquireSharedInterruptibly(int ard)方法;
方法實現原理請查看AQS源碼:
/**
* 首先檢測是否中斷.中斷後拋出異常
* 嘗試獲取許可,成功退出;失敗則進入AQS隊列,直至成功獲取或中斷
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 嘗試獲取鎖,返回剩餘共享鎖的數量;小於0則加入同步隊列,自旋
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared(arg)則會調用Semaphore中兩個同步器的實現方法;
如果獲取失敗則加入隊列等待喚醒;
非公平模式的實現
非公平實現都是首先查看是否有可獲取的許可,如果有則獲取成功,沒有則進隊列等待;利用此可以提高併發量,但可能導致某些線程長時間獲取不到許可,造成線程‘餓死’。
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
直接調用其父類Sync中非公平共享獲取
final int nonfairTryAcquireShared(int acquires) {
// 自旋直到無許可或者狀態位賦值成功
for (;;) {
int available = getState();
int remaining = available - acquires;
// 如果小於0則直接返回,否則利用CAS給AQS狀態位賦值
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
公平模式的實現
公平與非公平的區別在於始終按照AQS隊列FIFO的順序來的
protected int tryAcquireShared(int acquires) {
//自旋 CAS 實現線程安全
for (;;) {
// 判斷是否有前置任務排隊
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
以上兩種模式獲取失敗後都會調用doAcquireSharedInterruptibly(int arg);自旋等待獲取鎖 。源碼解析請看AQS解析。
3、void release()
公平和非公平使用相同的釋放
釋放許可
public void release() {
sync.releaseShared(1);
}
調用AQS中的releaseShared(int arg)
public final boolean releaseShared(int arg) {
// 調用Sync實現的tryReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared(int arg)是父類Sync實現。自旋直到釋放成功
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;
}
}
如果釋放許可成功,則調用AQS中的doReleaseShared()方法來喚醒AQS隊列中等待的線程
源碼解析請看AQS解析
這裏就是Semaphore的主要源碼。