Java線程池中execute()和submit()的區(qū)別全解析(源碼&實(shí)戰(zhàn))
前言
在 Java 并發(fā)編程里, ThreadPoolExecutor 是最常用的組件之一,而其中最常用的兩個(gè)方法就是:
execute(Runnable command)
submit(Callable/Runnable task)
看起來(lái)只有“是否有返回值”的差別?
其實(shí)遠(yuǎn)不止如此!
一、簡(jiǎn)單對(duì)比:一張表記住核心差異
| 項(xiàng)目 | execute() | submit() |
|---|---|---|
| 所屬接口 | Executor | ExecutorService |
| 接受參數(shù) | Runnable | Runnable / Callable |
| 是否有返回值 | ? 無(wú) | ? Future,可取消,可拿結(jié)果 |
| 異常處理 | ?異常直接拋出到線程的 UncaughtExceptionHandler | ?? 異常被捕獲放進(jìn) Future(不會(huì)直接打?。?/td> |
| 是否可取消 | ? 無(wú) | ? 可用 Future.cancel() |
| 是否支持批量匯總結(jié)果 | ? 不支持 | ? 支持 Future + CompletionService |
一句話總結(jié):
execute 用來(lái)“執(zhí)行任務(wù)”, submit 用來(lái)“管理任務(wù)”。
二、execute():輕量級(jí)任務(wù)執(zhí)行,不關(guān)心結(jié)果
executor.execute(() -> {
System.out.println("logging...");
int x = 1 / 0; // 會(huì)打印異常棧
});

特點(diǎn):
- 不關(guān)心返回值
- 異常會(huì)直接拋到控制臺(tái)
- 性能開(kāi)銷更小(不創(chuàng)建 FutureTask)
適用場(chǎng)景:
- 記錄訪問(wèn)日志
- 異步發(fā) MQ、埋點(diǎn)
- 刷緩存、通知等“扔過(guò)去就不管了”的任務(wù)
三、submit():帶狀態(tài)的任務(wù)管理,有返回值,可捕獲異常
Future<Integer> future = executor.submit(() -> {
return 123;
});
System.out.println(future.get()); // 結(jié)果 123

submit 最大的隱形區(qū)別:異常不會(huì)自動(dòng)拋出
Future<?> f = executor.submit(() -> {
throw new RuntimeException("error");
});
f.get(); // ExecutionException:異常從這里拋出

為什么?
因?yàn)?submit 內(nèi)部把任務(wù)包成了 FutureTask,它會(huì):
try {
call()
} catch (Throwable t) {
setException(t); // 保存到 Future
}
→ 所以異常不會(huì)直接爆出來(lái),而是等你 future.get() 再拋。
四、源碼視角:submit 底層其實(shí)也是調(diào)用 execute
submit 并不是獨(dú)立執(zhí)行任務(wù),它是:
- 把 Runnable/Callable 包裝成 FutureTask
- 調(diào)用 execute 執(zhí)行 FutureTask
源碼(摘自 AbstractExecutorService):
public <T> Future<T> submit(Callable<T> task) {
RunnableFuture<T> f = newTaskFor(task);
execute(f); // 注意這里!
return f;
}
而 execute 是線程池真正調(diào)度任務(wù)的入口。
五、異常處理的根本差別(重點(diǎn)!)
| 方法 | 任務(wù)內(nèi)部拋異常會(huì)怎樣? |
|---|---|
| execute | 異常會(huì)冒泡到線程頂層 → 默認(rèn)打印棧追蹤 |
| submit | 異常被 FutureTask 捕獲 → 不會(huì)打印,需要 future.get() 才能感知到 |
示例:submit 吃掉異常
executor.submit(() -> {
throw new RuntimeException("Boom!");
});
// 控制臺(tái)不會(huì)輸出任何異常
如果你不 get(),異常就徹底靜悄悄消失了,這在真實(shí)項(xiàng)目里非常危險(xiǎn)。
“為什么 submit() 會(huì)吃掉異常?怎么處理?”
答案:用 get() 捕獲 ExecutionException,從 cause 拿真正異常。
六、真實(shí)業(yè)務(wù)場(chǎng)景如何選擇?
? execute() 適用于:
- 不需要返回值
- 不需要任務(wù)取消
- 不關(guān)心任務(wù)是否失敗
典型業(yè)務(wù):
- 訪問(wèn)日志
- 埋點(diǎn)
- 異步通知
- 緩存刷新
? submit() 適用于:
- 需要匯總結(jié)果(并發(fā)調(diào)用多個(gè)服務(wù))
- 需要捕獲異常
- 需要任務(wù)狀態(tài)(是否完成、是否失?。?/li>
- 需要 cancel
典型業(yè)務(wù)(例如醫(yī)療系統(tǒng)):
- 藥審 + 過(guò)敏 + 醫(yī)保試算:并發(fā)執(zhí)行,返回綜合結(jié)果
- 大報(bào)表導(dǎo)出:后臺(tái)執(zhí)行 + 查詢狀態(tài) + 可取消
- 風(fēng)險(xiǎn)評(píng)估:多線程計(jì)算指標(biāo)后統(tǒng)一合并
- 第三方接口調(diào)用:需要捕獲異常、重試、降級(jí)
七、一個(gè)真實(shí)示例:醫(yī)療系統(tǒng)并發(fā)校驗(yàn)醫(yī)囑
Future<DrugAuditResult> drugF = pool.submit(() -> drugAudit());
Future<AllergyResult> allergyF = pool.submit(() -> checkAllergy());
Future<InsuranceResult> insF = pool.submit(() -> preCalcInsurance());
try {
DrugAuditResult d = drugF.get();
AllergyResult a = allergyF.get();
InsuranceResult i = insF.get();
return new Summary(d, a, i);
} catch (ExecutionException e) {
log.error("校驗(yàn)失敗", e.getCause());
throw new BusinessException("醫(yī)囑校驗(yàn)失敗");
}
這里必須用 submit,因?yàn)槟阋?/p>
- 拿返回值
- 捕獲異常
- 匯總結(jié)果
八、最終總結(jié)
execute 和 submit 的本質(zhì)區(qū)別:
| 關(guān)鍵點(diǎn) | execute | submit |
|---|---|---|
| 是否包裝 Future? | ? 否 | ? 是 |
| 異常如何處理? | 直接拋出 | 存 Future 內(nèi),get 時(shí)拋 |
| 是否有返回值? | 無(wú) | 有 |
| 是否可取消? | 否 | 是 |
| 是否適合任務(wù)編排/匯總結(jié)果? | ? | ? |
execute 用來(lái)“執(zhí)行任務(wù)”, submit 用來(lái)“管理任務(wù)”。
到此這篇關(guān)于Java線程池中execute()和submit()區(qū)別的文章就介紹到這了,更多相關(guān)Java線程池execute()和submit()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot4使用Sentinel進(jìn)行限流的操作方法
Sentinel是阿里巴巴開(kāi)源的流量控制組件,用于保障微服務(wù)架構(gòu)的穩(wěn)定性,它提供了多種規(guī)則類型,本文還介紹了如何在Spring Boot 4中集成Sentinel,以及Sentinel在不同版本的Spring Boot和Spring Cloud中的兼容性情況,感興趣的朋友跟隨小編一起看看吧2025-12-12
Java將文件內(nèi)容讀取為字符串的實(shí)現(xiàn)步驟
在Java編程中,經(jīng)常需要將文件內(nèi)容讀取為字符串進(jìn)行處理,比如配置文件讀取、文本數(shù)據(jù)解析等場(chǎng)景,不同版本的Java提供了多種實(shí)現(xiàn)方式,同時(shí)也有一些外部庫(kù)可以簡(jiǎn)化操作,本文給大家介紹了Java將文件內(nèi)容讀取為字符串的實(shí)現(xiàn)步驟,需要的朋友可以參考下2025-06-06
SimpleDateFormat在多線程下的安全問(wèn)題及解決
SimpleDateFormat類在Java中用于日期時(shí)間的格式化和解析,但在高并發(fā)環(huán)境下存在線程不安全的問(wèn)題,原因是它共享了一個(gè)Calendar對(duì)象,導(dǎo)致多個(gè)線程操作時(shí)出現(xiàn)沖突,為了解決這個(gè)問(wèn)題,可以使用局部變量、加鎖或使用線程安全的DateTimeFormatter類2026-03-03

