來源:https://blog.csdn.net/m0_46144826
概述
最近寫小玩具的時候用到了 CountDownLatch 計數器,然后順便想了想判斷執行緒池全部結束有多少種方法,
在網上搜了下,可能有些沒找到,但是我找到的有(所有方法都是在 ThreadPoolExecutor 執行緒池方法下測驗的):
isTerminated()判斷方式,在執行shutdown(),關閉執行緒池后,判斷是否所有任務已經完成,ThreadPoolExecutor的getCompletedTaskCount()方法,判斷完成任務數和全部任務數是否相等,CountDownLatch計數器,使用閉鎖計數來判斷是否全部完成,- 手動維護一個公共計數 ,原理和閉鎖類似,就是更加靈活,
- 使用
submit向執行緒池提交任務,Future判斷任務執行狀態,
好嘞,現在開始一個一個介紹優缺點和簡要原理;
先創建一個 static 執行緒池,后面好幾個例子就不一一創建了,全部用這個就行了:
/**
* 創建一個最大執行緒數是20的執行緒池
*/
public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
10, 20, 0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
然后再準備一個通用的睡眠方法:
/**
* 執行緒執行方法,隨機等待0到10秒
*/
private static void sleepMtehod(int index){
try {
long sleepTime = new Double(Math.random() * 10000).longValue();
Thread.sleep(sleepTime);
System.out.println("當前執行緒執行結束: " + index);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
這個方法就是為了測驗的時候區分執行緒執行完畢的下順序而已,
好嘞,準備完畢,現在開始,
isTerminated 方式
首先貼上測驗代碼:
private static void shutdownTest() throws Exception {
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> sleepMtehod(index));
}
pool.shutdown();
while (!pool.isTerminated()){
Thread.sleep(1000);
System.out.println("還沒停止,,,");
}
System.out.println("全部執行完畢");
}
這一種方式就是在主執行緒中進行回圈判斷,全部任務是否已經完成,
這里有兩個主要方法:
shutdown():啟動有序關閉,其中先前提交的任務將被執行,但不會接受任何新任務,如果已經關閉,呼叫沒有額外的作用,isTerminated():如果所有任務在關閉后完成,則回傳true,請注意,isTerminated從不是 true,除非shutdown或shutdownNow先被執行,
通俗點講,就是在執行全部任務后,對執行緒池進行 shutdown() 有序關閉,然后回圈判斷 isTerminated() ,執行緒池是否全部完成,
- 優點 :操作簡單,代碼更加簡單,
- 缺點 :需要關閉執行緒池,一般我在代碼中都是將執行緒池注入到 Spring 容器,然后各個組件中統一用同一個,當然不能關閉,
類似方法擴展:
shutdownNow():嘗試停止所有主動執行的任務,停止等待任務的處理,并回傳正在等待執行的任務串列,從此方法回傳時,這些任務將從任務佇列中洗掉,通過Thread.interrupt()取消任務,isShutdown():如果執行緒池已關閉,則回傳 true ,isTerminating():如果在shutdown()或shutdownNow()之后終止 ,但尚未完全終止,則回傳true,waitTermination(long timeout, TimeUnit unit):當前執行緒阻塞,直到等所有已提交的任務(包括正在跑的和佇列中等待的)執行完,或者等超時時間到,或者執行緒被中斷拋出例外;全部執行完回傳true,超時回傳false,也可以用這個方法代替isTerminated()進行判斷 ,
getCompletedTaskCount
還是一樣,貼上代碼:
private static void taskCountTest() throws Exception {
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> sleepMtehod(index));
}
//當執行緒池完成的執行緒數等于執行緒池中的總執行緒數
while (!(pool.getTaskCount() == pool.getCompletedTaskCount())) {
System.out.println("任務總數:" + pool.getTaskCount() + "; 已經完成任務數:" + pool.getCompletedTaskCount());
Thread.sleep(1000);
System.out.println("還沒停止,,,");
}
System.out.println("全部執行完畢");
}
還是一樣在主執行緒回圈判斷,主要就兩個方法:
getTaskCount():回傳計劃執行的任務總數,由于任務和執行緒的狀態可能在計算程序中動態變化,因此回傳的值只是一個近似值,getCompletedTaskCount():回傳完成執行的任務的大致總數,因為任務和執行緒的狀態可能在計算程序中動態地改變,所以回傳的值只是一個近似值,但是在連續的呼叫中并不會減少,
這個好理解,總任務數等于已完成任務數,就表示全部執行完畢,
- 優點 :完全使用了
ThreadPoolExecutor提供的方法,并且不必關閉執行緒池,避免了創建和銷毀帶來的損耗, - 缺點 :上面的解釋也看到了,使用這種判斷存在很大的限制條件;必須確定,在回圈判斷程序中,沒有新的任務產生,差不多意思就是,這個執行緒池只能在這條執行緒中使用,
其他 :
最后扯兩句,因為我用 main 方法運行的,跑完后 main 沒有結束,是因為非守護執行緒如果不終止,程式是不會結束的,而執行緒池 Worker 執行緒里寫了一個死回圈,而且被設定成了非守護執行緒,
CountDownLatch 計數器
這種方法是我比較常用的方法,先看代碼:
private static void countDownLatchTest() throws Exception {
//計數器,判斷執行緒是否執行結束
CountDownLatch taskLatch = new CountDownLatch(30);
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> {
sleepMtehod(index);
taskLatch.countDown();
System.out.println("當前計數器數量:" + taskLatch.getCount());
});
}
//當前執行緒阻塞,等待計數器置為0
taskLatch.await();
System.out.println("全部執行完畢");
}
這種方法,呃,應該是看起來比較高級的,我也不知道別的大佬怎么寫的,反正我就用這個,
這個方法需要介紹下這個工具類 CountDownLatch ,先把這種方式的優缺點寫了,后面再詳細介紹這個類,
- 優點 :代碼優雅,不需要對執行緒池進行操作,將執行緒池作為 Bean 的情況下有很好的使用場景,
- 缺點 :需要提前知道執行緒數量;性能確實,呃呃呃呃呃,差了點,哦對了,還需要在執行緒代碼塊內加上例外判斷,否則在
countDown之前發生例外而沒有處理,就會導致主執行緒永遠阻塞在 await,
CountDownLatch 概述
CountDownLatch 是 JDK 提供的一個同步工具,它可以讓一個或多個執行緒等待,一直等到其他執行緒中執行完成一組操作,
常用的方法有 countDown 方法和 await 方法,CountDownLatch 在初始化時,需要指定用給定一個整數作為計數器,
當呼叫 countDown 方法時,計數器會被減1;當呼叫 await 方法時,如果計數器大于0時,執行緒會被阻塞,一直到計數器被 countDown 方法減到0時,執行緒才會繼續執行,
計數器是無法重置的,當計數器被減到0時,呼叫 await 方法都會直接回傳,
維護一個公共計數
這種方式其實和 CountDownLatch 原理類似,
先維護一個靜態變數
private static int taskNum = 0;
然后在執行緒任務結束時,進行靜態變數操作:
private static void staticCountTest() throws Exception {
Lock lock = new ReentrantLock();
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> {
sleepMtehod(index);
lock.lock();
taskNum++;
lock.unlock();
});
}
while(taskNum < 30) {
Thread.sleep(1000);
System.out.println("還沒停止,,,當前完成任務數:" + taskNum);
}
System.out.println("全部執行完畢");
}
其實就是加鎖計數,回圈判斷,
- 優點 :手動維護方式更加靈活,對于一些特殊場景可以手動處理,
- 缺點 :和
CountDownLatch相比,一樣需要知道執行緒數目,但是代碼實作比較麻煩,相對于靈活這一個優勢,貌似投入產出并不對等,
Future 判斷任務執行狀態
Future 是用來裝載執行緒結果的,不過,用這個來進行判斷寫代碼總感覺怪怪的,
因為 Future 只能裝載一條執行緒的回傳結果,多條執行緒總不能用 List 在接收 Future ,
這里就開一個執行緒做個演示:
private static void futureTest() throws Exception {
Future<?> future = pool.submit(() -> sleepMtehod(1));
while (!future.isDone()){
Thread.sleep(500);
System.out.println("還沒停止,,,");
}
System.out.println("全部執行完畢");
}
這種方式就不寫優缺點了,因為 Future 的主要使用場景并不是用于判斷任務執行狀態,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/525111.html
標籤:其他
上一篇:基數排序法
