semaphore也就是我們常說的信號燈,semaphore可以控制同時訪問的線程個數,通過acquire獲取一個許可,如果沒有就等待,通過release釋放一個許可。有點類似限流的作用。叫信號燈的原因也和他的用處有關,比如某商場就5個停車位,每個停車位只能停一輛車,如果這個時候來了10輛車,必須要等前面有空的車位才能進入。
public class SemaphoreDemo { //限流(AQS) //permits; 令牌(5) //公平和非公平 static class Car extends Thread{ private int num; private Semaphore semaphore; public Car(int num, Semaphore semaphore) { this.num = num; this.semaphore = semaphore; } public void run(){ try { semaphore.acquire(); //獲得一個令牌, 如果拿不到令牌,就會阻塞 System.out.println("第"+num+" 搶占一個車位"); Thread.sleep(2000); System.out.println("第"+num+" 開走嘍"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Semaphore semaphore=new Semaphore(5); for(int i=0;i<10;i++){ new Car(i,semaphore).start(); } } }
結果:
第0 搶占一個車位
第1 搶占一個車位
第2 搶占一個車位
第3 搶占一個車位
第4 搶占一個車位
第0 開走嘍
第1 開走嘍
第5 搶占一個車位
第6 搶占一個車位
第2 開走嘍
第3 開走嘍
第7 搶占一個車位
第4 開走嘍
第8 搶占一個車位
第9 搶占一個車位
第6 開走嘍
第8 開走嘍
第9 開走嘍
第7 開走嘍
第5 開走嘍
Semaphore源碼分析
從Semaphore的功能來看,我們基本能猜測到它的底層實現一定是基於AQS的共享所,因為需要實現多個線程共享一個領排池
創建Semaphore 實例的時候,需要一個參數permits,這個基本上可以確定是設置給AQS 的state 的,然后每個線程調用acquire 的時候,執行state = state -1,release 的時候執行state = state + 1,當然,acquire 的時候,如果state = 0,說明沒有資源了,需要等待其他線程release。
Semaphore 分公平策略和非公平策略
static final class FairSync extends Sync { private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) { super(permits); }
protected int tryAcquireShared(int acquires) {
for (;;) { // 區別就在於是不是會先判斷是否有線程在排隊,然后才進行 CAS 減操作
if (hasQueuedPredecessors()) return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
使用場景
Semaphore比較常見的就是用來做限流操作了。