1. 程式人生 > >Java併發程式設計基礎三板斧之Semaphore

Java併發程式設計基礎三板斧之Semaphore

# 引言 最近可以進行個稅申報了,還沒有申報的同學可以趕緊去試試哦。不過我反正是從上午到下午一直都沒有成功的進行申報,一進行申報 就返回“當前訪問人數過多,請稍後再試”。為什麼有些人就能夠申報成功,有些人就直接返回失敗。這很明顯申報處理資源是有限的, 只能等別人處理完了在來處理你的,你如果運氣好可能重試幾次就輪到你了,如果運氣不好可能重試一天也可能輪不到你。 我反正已經是放棄了,等到夜深人靜的時候再來試試。作為一個程式設計師我們肯定知道這是個稅申請**app**的限流操作,如果還有不懂什麼 是限流操作的可以參考下這個文章[《高併發系統三大利器之限流》](https://mp.weixin.qq.com/s/AKWHHzoJgalTp3Fxvox2_A)。 比如個稅申報系統每臺機器只最多分別只能處理`1000`個請求,再多的請求就會把機器打掛。如果是多餘的請求就把這些請求拒絕掉。直接給你返回一句溫馨提示:“當前訪問人數過多,請稍後再試”,如果要實現這個功能大家想想可以通過哪些方法演算法來實現。 # 共享鎖、獨佔鎖 學習`semaphore`之前我們必須要先了解下什麼是共享鎖。在上一篇文章[《Java高併發程式設計基礎之AQS》](https://mp.weixin.qq.com/s/Scz_puodkdtoA6Zz7nh4uA)我們介紹了公平鎖於非公平鎖的區別。 - 共享鎖:它是允許多個執行緒同時獲取鎖,併發的訪問共享資源 - 獨佔鎖:也有人把它叫做“獨享鎖”,它是是獨佔的,排他的,只能被一個執行緒可持有, 當獨佔鎖已經被某個執行緒持有時,其他執行緒只能等待它被釋放後,才能去爭鎖,並且同一時刻只有一個執行緒能爭鎖成功。 # 什麼是Semaphore 在《**Java併發程式設計藝術**》(微信搜【**java金融**】回覆**電子書**可以免費獲取PDF版本)這一書中是這麼說的: >Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,以保證合理的使用公共資源。很多年以來,我都覺得從字面上很難理解Semaphore所表達的含義,只能把它比作是控制流量的紅綠燈,比如XX馬路要限制流量,只允許同時有一百輛車在這條路上行使,其他的都必須在路口等待,所以前一百輛車會看到綠燈,可以開進這條馬路,後面的車會看到紅燈,不能駛入XX馬路,但是如果前一百輛中有五輛車已經離開了XX馬路,那麼後面就允許有5輛車駛入馬路,這個例子裡說的車就是執行緒,駛入馬路就表示執行緒在執行,離開馬路就表示執行緒執行完成,看見紅燈就表示執行緒被阻塞,不能執行。 - `Semaphore`機制是提供給執行緒搶佔式獲取許可,所以他可以實現公平或者非公平,類似於`ReentrantLock`。 說了這麼多我們來個實際的例子看一看,比如我們去停車場停車,停車場總共只有`5`個車位,但是現在有`8`輛汽車來停車,剩下的`3`輛汽車要麼等其他汽車開走後進行停車,或者去找別的停車位? ```java /** * @author: 公眾號【Java金融】 */ public class SemaphoreTest { public static void main(String[] args) throws InterruptedException { // 初始化五個車位 Semaphore semaphore = new Semaphore(5); // 等所有車子 final CountDownLatch latch = new CountDownLatch(8); for (int i = 0; i < 8; i++) { int finalI = i; if (i == 5) { Thread.sleep(1000); new Thread(() -> { stopCarNotWait(semaphore, finalI); latch.countDown(); }).start(); continue; } new Thread(() -> { stopCarWait(semaphore, finalI); latch.countDown(); }).start(); } latch.await(); log("總共還剩:" + semaphore.availablePermits() + "個車位"); } private static void stopCarWait(Semaphore semaphore, int finalI) { String format = String.format("車牌號%d", finalI); try { semaphore.acquire(1); log(format + "找到車位了,去停車了"); Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } finally { semaphore.release(1); log(format + "開走了"); } } private static void stopCarNotWait(Semaphore semaphore, int finalI) { String format = String.format("車牌號%d", finalI); try { if (semaphore.tryAcquire()) { log(format + "找到車位了,去停車了"); Thread.sleep(10000); log(format + "開走了"); semaphore.release(); } else { log(format + "沒有停車位了,不在這裡等了去其他地方停車去了"); } } catch (Exception e) { e.printStackTrace(); } } public static void log(String content) { // 格式化 DateTimeFormatter fmTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 當前時間 LocalDateTime now = LocalDateTime.now(); System.out.println(now.format(fmTime) + " "+content); } } ``` ```java 2021-03-01 18:54:57 車牌號0找到車位了,去停車了 2021-03-01 18:54:57 車牌號3找到車位了,去停車了 2021-03-01 18:54:57 車牌號2找到車位了,去停車了 2021-03-01 18:54:57 車牌號1找到車位了,去停車了 2021-03-01 18:54:57 車牌號4找到車位了,去停車了 2021-03-01 18:54:58 車牌號5沒有停車位了,不在這裡等了去其他地方停車去了 2021-03-01 18:55:07 車牌號7找到車位了,去停車了 2021-03-01 18:55:07 車牌號6找到車位了,去停車了 2021-03-01 18:55:07 車牌號2開走了 2021-03-01 18:55:07 車牌號0開走了 2021-03-01 18:55:07 車牌號3開走了 2021-03-01 18:55:07 車牌號4開走了 2021-03-01 18:55:07 車牌號1開走了 2021-03-01 18:55:17 車牌號7開走了 2021-03-01 18:55:17 車牌號6開走了 2021-03-01 18:55:17 總共還剩:5個車位 ``` 從輸出結果我們可以看到`車牌號5`這輛車看見沒有車位了,就不在這個地方傻傻的等了,而是去其他地方了,但是`車牌號6`和`車牌號7`分別需要等到車庫開出兩輛車空出兩個車位後才停進去。這就體現了`Semaphore` 的`acquire` 方法如果沒有獲取到憑證它就會阻塞,而`tryAcquire`方法如果沒有獲取到憑證不會阻塞的。 # semaphore在dubbo中的應用 在`Dubbo`中可以給`Provider`配置執行緒池大小來控制系統提供服務的最大並行度,預設是`200`。