Semaphore
一個計數信號量。從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然後再獲取該許可。每個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,並採取相應的行動。
其實Semaphore就是維護了一個共享鎖,通過state來決定同時可以多少個線程獲取共享鎖。
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
acquire就是獲取共享鎖。
acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted()) //線程被中斷拋出中斷異常
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //嘗試獲取鎖
doAcquireSharedInterruptibly(arg);//調用方法doAcquireSharedInterruptibly
}
doAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);//// 創建”當前線程“的Node節點,且Node中記錄的鎖是”共享鎖“類型;並將該節點添加到CLH隊列末尾。
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor(); //獲取前一個節點
if (p == head) { //如果上一個節點是頭節點
int r = tryAcquireShared(arg);//嘗試獲取鎖(上一個是頭節點,那麼可能此時上一個節點已經成功獲取了鎖,所以嘗試獲取一下)
if (r >= 0) { //獲取成功
setHeadAndPropagate(node, r); //設置頭節點
p.next = null; // help GC
failed = false;
return;
}
}
// 當前線程一直等待,直到獲取到共享鎖。
// 如果線程在等待過程中被中斷過,則再次中斷該線程(還原之前的中斷狀態)。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
因此Semaphore就是同時可以讓多個線程去獲取共享鎖,如果線程達到指定閥值,那麼就等待,一直等到其它線程調用了release釋放。
所以,Semaphore經常用來控制公共資源的併發訪問。比如對某一個文件限制只能同時10個線程進行讀取。
public class SemaphoreTest {
static Semaphore semaphore = new Semaphore(10);
public static void main(String[] args) {
for(int i = 0 ; i < 100; i++) {
new ReadFile().start();
}
}
static class ReadFile extends Thread{
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "開始讀取文件");
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
// System.out.println(Thread.currentThread().getName() + "釋放了一個許可");
//semaphore.release();
}
}
}
}
某次運行結果:
Thread-0開始讀取文件
Thread-1開始讀取文件
Thread-4開始讀取文件
Thread-2開始讀取文件
Thread-3開始讀取文件
Thread-5開始讀取文件
Thread-7開始讀取文件
Thread-9開始讀取文件
Thread-6開始讀取文件
Thread-8開始讀取文件
結果是隻有是個線程開始讀取,其它線程都在等待,沒有打印出來。如果把上面的註釋代碼去掉。那麼有釋放鎖,就會讓其它線程可以讀取。