同步工具類 CountDownLatch 和 CyclicBarrier
在開發中,一些非同步操作會明顯加快執行速度帶來更好的體驗,但同時也增加了開發的複雜度,想了用好多執行緒,就必須從這些方面去了解
- 執行緒的
wait() notify() notifyall()
方法 執行緒非同步返回
Future
ThreadLocal
類- 執行緒池
ThreadPoolExecutor
同步工具類
CountDownLatch
,CyclicBarrier
,Semaphore
,Phaser
,Exchanger
估計上面每一個對於 2~3 年的 java 同學來說都是惡夢,比較難以理解,本文簡單說下 CountDownLatch
和 CyclicBarrier
CountDownLatch
CountDownLatch
一般在執行時間較長的可分解的任務中比較常用,算是同步工具類中最容易理解的一個。
示例一:
一個簡單的例子:有一個 100 萬的 excel 資料匯出,需要從資料庫中查出資料,並封裝成 excel 資料然後輸出到前端。
稍微分析可以知道這個操作肯定會很費時間,它的瓶頸出在查詢資料庫資料和寫 excel 上,如果我每 10 萬資料一頁讀資料庫並寫 excel 成一個檔案,最後把所有的 excel 使用 zip 打包,使用多執行緒,由於讀資料庫並不會加鎖,效能將會有一個量級的提升(有實踐過),這時會有一個問題,我啟動多個執行緒後,主執行緒如何才能知道所有的執行緒都完成了呢,只有在所有執行緒都完成了後,才能對所有 excel 檔案進行打包,這時可以用 CountDownLatch
// Excel 執行緒 class ExcelThread extend Thread{ private CountDownLatch countDownLatch; public ExcelThread(CountDownLatch countDownLatch){ this.countDownLatch = countDownLatch; } public void run(){ try{ // do query db & write excel }finally{ countDownLatch.countDown(); } } } public static void main(){ //假定生成 3 個 excel CountDownLatch countdownlatch = new CountDownLatch(3); foreach : new ExcelThread(countdownlatch).start(); countDownLatch.await(); // do zip compress & down }
示例二:
如果用過 IDM 下載工具,看它的下載進度一定知道它是多執行緒下載的,自動分成了多段,其實用 java 的 RandomAccessFile
配合 CountDownlatch
一樣可以做到多執行緒下載,可以例項化多個 RandomAccessFile
然後讓其指向檔案不同的檔案位置,然後向裡面填充資料即可,主執行緒在分執行緒全部完成後檢驗檔案完整性。
當然 IDM 做得更好,它會其它執行緒都完成了,如果某一段還卡著的話,繼續分隔,同樣多執行緒下載,當然速度就快了。
示例三:
對於CountDownLatch,其他執行緒為遊戲玩家,比如王者榮耀,主執行緒為控制遊戲開始的執行緒。在所有的玩家都準備好之前,主執行緒是處於等待狀態的,也就是遊戲不能開始。當所有的玩家準備好之後,下一步的動作實施者為主執行緒,即開始遊戲。
CyclicBarrier
相比如 CountDownLatch
是在主執行緒等待,CyclicBarrier
是子執行緒相互等待,而主執行緒早就已經結束了,並且在子執行緒相互等待的同時,可以附帶一個 CountDownLatch
類似功能的執行緒,等所有子執行緒都完成了再操作,並且 CyclicBarrier
是可重用的,說這麼多,看它的使用方式就知道什麼意思了。
五人六足之類的遊戲不知道讀者玩過沒,每個玩家是一個執行緒,必須互相等待對方準備好才可以進行下一步操作,其中也可以來一位指揮員他等到所有同學完成準備後下發指令,左腳,右腳。。。虛擬碼如下:
// 遊戲玩家執行緒
class GamePerson extend Thread{
private CyclicBarrier cyclicBarrier;
public GamePerson(CyclicBarrier cyclicBarrier){
this.cyclicBarrier = cyclicBarrier;
}
public void run(){
// 走下一步的準備階段
prepareNextStep();
cyclicBarrier.await();
// 走下一步
nextStep();
}
}
// 主執行緒
public static void main(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
//構建 5 個遊戲玩家
foreach:
new GamePerson(cyclicBarrier);
// 遊戲開始,並開始計時
startGame();beginCountTime();
foreach:gamepersons
gameperson.start();
}
// 主執行緒,新增指揮員
class Leader extend Thread{
public void run(){
System.out.println(“開始走”);
}
}
// 主執行緒
public static void main(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new Leader());
//構建 5 個遊戲玩家
foreach:
new GamePerson(cyclicBarrier);
// 遊戲開始,並開始計時
startGame();beginCountTime();
foreach:gamepersons
gameperson.start();
}
// 前面的例子只能跨一步,如果需要重用 CyclicBarrier 需要把主執行緒也當做同步物件,程式碼如下
public static void main(){
CyclicBarrier cyclicBarrier = new CyclicBarrier(6);
//構建 5 個遊戲玩家
foreach:
new GamePerson(cyclicBarrier);
// 遊戲開始,並開始計時
startGame();beginCountTime();
//假設讓它們走 10 步
for(int i=0;i<10;i++){
cyclicBarrier.reset();
foreach:gamepersons
gameperson.start();
cyclicBarrier.await();
}
}
一點小推廣
創作不易,希望可以支援下我的開源軟體,及我的小工具,歡迎來 gitee 點星,fork ,提 bug 。
Excel 通用匯入匯出,支援 Excel 公式
部落格地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi
使用模板程式碼 ,從資料庫生成程式碼 ,及一些專案中經常可以用到的小工具
部落格地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-ma