Java異步判斷線程池所有任務(wù)是否執(zhí)行完成的操作方法
1.使用ExecutorService和CountDownLatch的方法示例
在Java中,當(dāng)我們使用線程池(如ExecutorService)來執(zhí)行異步任務(wù)時(shí),常常需要知道所有任務(wù)是否都已經(jīng)完成。ExecutorService接口提供了幾種方式來處理這種情況,但最常用的是shutdown()和awaitTermination()方法的組合,或者使用Future和CompletionService。這里我將提供一個(gè)使用ExecutorService和CountDownLatch的示例,因?yàn)?code>CountDownLatch提供了一種直觀的方式來等待一組線程完成。
首先,我們定義幾個(gè)任務(wù),然后使用ExecutorService來異步執(zhí)行它們,并使用CountDownLatch來等待所有任務(wù)完成。
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) throws InterruptedException {
// 創(chuàng)建一個(gè)包含固定數(shù)量線程的線程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 定義任務(wù)數(shù)量
int taskCount = 10;
// 使用CountDownLatch來等待所有任務(wù)完成
final CountDownLatch latch = new CountDownLatch(taskCount);
// 提交任務(wù)到線程池
for (int i = 0; i < taskCount; i++) {
int taskId = i;
executorService.submit(() -> {
// 模擬任務(wù)執(zhí)行
try {
Thread.sleep(1000); // 假設(shè)每個(gè)任務(wù)需要1秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任務(wù) " + taskId + " 完成");
// 每完成一個(gè)任務(wù),計(jì)數(shù)減一
latch.countDown();
});
}
// 等待所有任務(wù)完成
System.out.println("等待所有任務(wù)完成...");
latch.await(); // 阻塞當(dāng)前線程,直到latch的計(jì)數(shù)達(dá)到零
System.out.println("所有任務(wù)完成!");
// 關(guān)閉線程池
executorService.shutdown();
// 可選:等待線程池中的線程都執(zhí)行完畢
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 線程池沒有在規(guī)定時(shí)間內(nèi)關(guān)閉,則強(qiáng)制關(guān)閉
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// 當(dāng)前線程在等待過程中被中斷
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}在這個(gè)例子中,我們首先創(chuàng)建了一個(gè)固定大小的線程池(這里使用4個(gè)線程)。然后,我們定義了一個(gè)CountDownLatch,其計(jì)數(shù)被初始化為任務(wù)的數(shù)量(這里為10)。對(duì)于每個(gè)任務(wù),我們都向線程池提交了一個(gè)Runnable,其中包含了任務(wù)的執(zhí)行邏輯和latch.countDown()調(diào)用,以確保每次任務(wù)完成時(shí)都會(huì)減少CountDownLatch的計(jì)數(shù)。
主線程通過調(diào)用latch.await()來等待,直到所有任務(wù)都調(diào)用了countDown()(即計(jì)數(shù)達(dá)到零),然后才能繼續(xù)執(zhí)行。這確保了主線程會(huì)等待所有任務(wù)完成后再繼續(xù)。
最后,我們關(guān)閉了線程池,并通過調(diào)用awaitTermination()來可選地等待線程池中的所有線程都執(zhí)行完畢。如果線程池沒有在指定時(shí)間內(nèi)關(guān)閉,則調(diào)用shutdownNow()來嘗試立即停止所有正在執(zhí)行的任務(wù)。
這個(gè)示例提供了處理異步任務(wù)并等待它們完成的一種有效方式,適用于需要等待所有任務(wù)完成再繼續(xù)的場(chǎng)景。
2.使用ExecutorService的invokeAll方法和Future列表的方法示例
除了使用CountDownLatch之外,還有其他方法可以判斷線程池中的所有任務(wù)是否執(zhí)行完成。以下是一個(gè)使用ExecutorService的invokeAll方法和Future列表的示例,這種方法適用于我們有一組已知的任務(wù)(Callable)需要并行執(zhí)行,并且我們需要等待所有任務(wù)完成并獲取它們的結(jié)果。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolFutureExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 創(chuàng)建一個(gè)包含固定數(shù)量線程的線程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 創(chuàng)建一個(gè)Callable任務(wù)列表
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(() -> {
// 模擬任務(wù)執(zhí)行
Thread.sleep(1000); // 假設(shè)每個(gè)任務(wù)需要1秒
return "任務(wù) " + taskId + " 完成";
});
}
// 使用invokeAll提交所有任務(wù),這將返回一個(gè)Future列表
List<Future<String>> futures = executorService.invokeAll(tasks);
// 遍歷Future列表,獲取每個(gè)任務(wù)的結(jié)果
for (Future<String> future : futures) {
// get()會(huì)阻塞,直到對(duì)應(yīng)的任務(wù)完成
System.out.println(future.get());
}
// 關(guān)閉線程池
executorService.shutdown();
// 可選:等待線程池中的線程都執(zhí)行完畢
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 線程池沒有在規(guī)定時(shí)間內(nèi)關(guān)閉,則強(qiáng)制關(guān)閉
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// 當(dāng)前線程在等待過程中被中斷
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
// 注意:這里使用了Lambda表達(dá)式和方法引用來簡(jiǎn)化Callable的創(chuàng)建
// 實(shí)際使用中,你可能需要實(shí)現(xiàn)Callable接口或使用匿名內(nèi)部類在這個(gè)例子中,我們創(chuàng)建了一個(gè)ExecutorService和一個(gè)Callable任務(wù)列表。每個(gè)Callable任務(wù)都會(huì)返回一個(gè)字符串,表示任務(wù)完成的信息。我們使用invokeAll方法提交了所有任務(wù),并立即獲得了一個(gè)Future列表,每個(gè)Future都代表了一個(gè)任務(wù)的執(zhí)行結(jié)果。
然后,我們遍歷這個(gè)Future列表,并對(duì)每個(gè)Future調(diào)用get()方法。get()方法會(huì)阻塞當(dāng)前線程,直到對(duì)應(yīng)的任務(wù)完成并返回結(jié)果。這樣,我們就能確保在繼續(xù)執(zhí)行之前,所有任務(wù)都已經(jīng)完成。
最后,我們關(guān)閉了線程池,并等待所有線程都執(zhí)行完畢(或超時(shí)后強(qiáng)制關(guān)閉)。
請(qǐng)注意,雖然這個(gè)示例使用了Callable和Future,但它并沒有直接提供一個(gè)“是否所有任務(wù)都已完成”的布爾值。然而,通過遍歷Future列表并調(diào)用get(),我們實(shí)際上已經(jīng)達(dá)到了等待所有任務(wù)完成的效果。如果我們只需要知道是否所有任務(wù)都已開始執(zhí)行(而不是等待它們完成),那么我們可能需要采用不同的策略,比如使用execute方法結(jié)合其他同步機(jī)制(如CountDownLatch)。
3.使用ExecutorService來異步執(zhí)行多個(gè)Callable任務(wù)方法示例
以下是一個(gè)詳細(xì)完整的代碼示例,該示例使用了ExecutorService來異步執(zhí)行多個(gè)Callable任務(wù),并通過遍歷Future列表來等待所有任務(wù)完成并獲取它們的結(jié)果。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolFutureCompleteExample {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)包含固定數(shù)量線程的線程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 創(chuàng)建一個(gè)Callable任務(wù)列表
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
// 模擬任務(wù)執(zhí)行
TimeUnit.SECONDS.sleep(1); // 假設(shè)每個(gè)任務(wù)需要1秒
return "任務(wù) " + taskId + " 完成";
}
});
// 或者使用Lambda表達(dá)式(如果你使用的是Java 8或更高版本)
// tasks.add(() -> {
// TimeUnit.SECONDS.sleep(1);
// return "任務(wù) " + taskId + " 完成";
// });
}
try {
// 使用invokeAll提交所有任務(wù),這將返回一個(gè)Future列表
List<Future<String>> futures = executorService.invokeAll(tasks);
// 遍歷Future列表,獲取每個(gè)任務(wù)的結(jié)果
for (Future<String> future : futures) {
// get()會(huì)阻塞,直到對(duì)應(yīng)的任務(wù)完成
System.out.println(future.get());
}
// 關(guān)閉線程池
executorService.shutdown();
// 等待線程池中的所有線程都執(zhí)行完畢(可選)
// 注意:由于我們已經(jīng)調(diào)用了invokeAll并等待了所有Future的完成,這一步通常是多余的
// 但為了完整性,我還是展示了如何等待線程池關(guān)閉
boolean terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
if (!terminated) {
// 如果線程池沒有在規(guī)定時(shí)間內(nèi)關(guān)閉,則強(qiáng)制關(guān)閉
System.err.println("線程池沒有在規(guī)定時(shí)間內(nèi)關(guān)閉,嘗試強(qiáng)制關(guān)閉...");
executorService.shutdownNow();
// 注意:shutdownNow()不保證已經(jīng)提交的任務(wù)會(huì)被取消
// 它會(huì)嘗試停止正在執(zhí)行的任務(wù),但已經(jīng)開始執(zhí)行的任務(wù)可能無法被中斷
}
} catch (InterruptedException | ExecutionException e) {
// 處理異常
e.printStackTrace();
// 如果當(dāng)前線程在等待過程中被中斷,嘗試關(guān)閉線程池
if (!executorService.isShutdown()) {
executorService.shutdownNow();
}
// 根據(jù)需要,可能還需要重新設(shè)置中斷狀態(tài)
Thread.currentThread().interrupt();
}
}
}在這個(gè)示例中,我使用了傳統(tǒng)的匿名內(nèi)部類來創(chuàng)建Callable任務(wù)(同時(shí)也提供了Lambda表達(dá)式的注釋),以便與各種Java版本兼容。然而,如果我們正在使用Java 8或更高版本,我強(qiáng)烈推薦我們使用Lambda表達(dá)式來簡(jiǎn)化代碼。
請(qǐng)注意,invokeAll方法會(huì)阻塞調(diào)用它的線程,直到所有任務(wù)都完成,或者直到等待超時(shí)(如果我們提供了超時(shí)時(shí)間)。但是,在這個(gè)示例中,我們沒有為invokeAll提供超時(shí)時(shí)間,因此它會(huì)一直等待,直到所有任務(wù)都完成。
另外,請(qǐng)注意,在catch塊中,如果捕獲到InterruptedException,我們檢查了線程池是否已經(jīng)被關(guān)閉(使用isShutdown方法)。如果沒有,我們調(diào)用shutdownNow方法來嘗試關(guān)閉線程池并停止正在執(zhí)行的任務(wù)。然而,需要注意的是,shutdownNow方法并不保證能夠停止所有已經(jīng)開始執(zhí)行的任務(wù),因?yàn)槟承┤蝿?wù)可能無法被中斷。
最后,如果在捕獲到InterruptedException后,我們確定當(dāng)前線程需要被重新中斷(比如,我們?cè)谝粋€(gè)循環(huán)中等待某個(gè)條件,而中斷是用來退出循環(huán)的),那么我們應(yīng)該調(diào)用Thread.currentThread().interrupt()來重新設(shè)置中斷狀態(tài)。在這個(gè)示例中,我們沒有這樣做,因?yàn)?code>main方法不需要重新中斷。但是,在更復(fù)雜的場(chǎng)景中,這可能是必要的。
到此這篇關(guān)于Java異步判斷線程池所有任務(wù)是否執(zhí)行完成的方法的文章就介紹到這了,更多相關(guān)Java異步判斷線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot項(xiàng)目用maven插件打包時(shí)報(bào)錯(cuò)的解決方法
文章介紹了解決Spring Boot項(xiàng)目使用Maven插件打包時(shí)遇到JDK版本無效問題的步驟,本文分步驟給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2026-01-01
RedisKey的失效監(jiān)聽器KeyExpirationEventMessageListener問題
這篇文章主要介紹了RedisKey的失效監(jiān)聽器KeyExpirationEventMessageListener問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Java使用跳轉(zhuǎn)結(jié)構(gòu)實(shí)現(xiàn)隊(duì)列和棧流程詳解
這篇文章主要介紹了Java使用跳轉(zhuǎn)結(jié)構(gòu)實(shí)現(xiàn)隊(duì)列和棧流程,連續(xù)結(jié)構(gòu)和跳轉(zhuǎn)結(jié)構(gòu)是數(shù)據(jù)結(jié)構(gòu)中常見的兩種基本數(shù)據(jù)結(jié)構(gòu),而我們本次的主角棧和隊(duì)列都 既可以使用使用跳轉(zhuǎn)結(jié)構(gòu)實(shí)現(xiàn)也可以使用連續(xù)結(jié)構(gòu)實(shí)現(xiàn)2023-04-04
深入Ajax代理的Java Servlet的實(shí)現(xiàn)詳解
本篇文章是對(duì)Ajax代理的Java Servlet的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型
這篇文章主要介紹了淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12
Nacos動(dòng)態(tài)配置管理機(jī)制方式
這篇文章主要介紹了Nacos動(dòng)態(tài)配置管理機(jī)制方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07

