Java線程池FutureTask實(shí)現(xiàn)原理詳解
前言
線程池可以并發(fā)執(zhí)行多個(gè)任務(wù),有些時(shí)候,我們可能想要跟蹤任務(wù)的執(zhí)行結(jié)果,甚至在一定時(shí)間內(nèi),如果任務(wù)沒(méi)有執(zhí)行完成,我們可能還想要取消任務(wù)的執(zhí)行,為了支持這一特性,ThreadPoolExecutor提供了 FutureTask 用于追蹤任務(wù)的執(zhí)行和取消。本篇介紹FutureTask的實(shí)現(xiàn)原理。
類(lèi)視圖
為了更好的理解FutureTask的實(shí)現(xiàn)原理,這里先提供幾個(gè)重要接口和類(lèi)的結(jié)構(gòu),如下圖所示:

RunnableAdapter
ThreadPoolExecutor提供了submit接口用于提交任務(wù),submit支持Runnable和Callable兩種不同的接口,為了提供統(tǒng)一的對(duì)外接口,jdk在內(nèi)部把Runnable給包裝成了一個(gè)Callable,這一切是通過(guò)RunnableAdapter這個(gè)適配器來(lái)實(shí)現(xiàn)的。如下為RunnableAdapter的源碼:
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
RunnableAdapter是Callable 的實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)了call方法,而call方法僅僅是調(diào)用task.run(),然后return result,這樣就能夠確保在內(nèi)部只需要統(tǒng)一處理Callable接口。
FutureTask實(shí)現(xiàn)原理
通過(guò)上一小節(jié)的了解,我們知道提交的Runnable任務(wù)在內(nèi)部統(tǒng)一被轉(zhuǎn)換為Callable任務(wù)。查看submit方法的返回值,為一個(gè)Future,實(shí)際上這個(gè)Futrue為FutureTask實(shí)例,通過(guò)此實(shí)例,調(diào)用get方法,可以阻塞當(dāng)前線程,直到任務(wù)運(yùn)行完畢,返回結(jié)果。
整個(gè)調(diào)用鏈條如下所示:
worker thread -> futureTask.run() -> callable.call() -> task.run()
如果提交的是Callable任務(wù),則只有前面三個(gè)調(diào)用。
為了更好的展示整個(gè)流程,下面舉例演示一遍執(zhí)行流程。
1、 向線程池submit一個(gè)Callable任務(wù)(Runnable也會(huì)被轉(zhuǎn)為Callable), 這時(shí)候Callable被傳入一個(gè)FutureTask實(shí)例中,如下所示:

2、線程池使用一個(gè)線程,執(zhí)行這個(gè) FutureTask 任務(wù),

線程執(zhí)行任務(wù)過(guò)程比較簡(jiǎn)單,最終會(huì)調(diào)用Callable.call()或者是 Runnable.run()方法,然后得到一個(gè)結(jié)果,把結(jié)果存儲(chǔ)在FutureTask實(shí)例的outcome屬性中,同時(shí)把狀態(tài)修改為NORMAL,表明任務(wù)已經(jīng)執(zhí)行完畢,可以獲取結(jié)果了。
我們假設(shè)在執(zhí)行 callable.call() 過(guò)程中有多個(gè)線程調(diào)用了 同個(gè)FutureTask實(shí)例的get方法,這時(shí)候,這些線程會(huì)被阻塞,存于一個(gè)棧中, 如下圖所示:

線程1,2,3調(diào)用FutureTask.get方法,由于任務(wù)未執(zhí)行結(jié)束,這時(shí)候,三個(gè)線程都將被阻塞休眠,F(xiàn)utureTask中有一個(gè)棧,用于存放等待線程,棧頂指針為 FutureTask.waiters引用,當(dāng)任務(wù)執(zhí)行完畢后,會(huì)迭代喚醒整個(gè)棧中的線程,這時(shí)候,各個(gè)線程都將被喚醒,并且可以順利拿到任務(wù)的執(zhí)行結(jié)果(執(zhí)行結(jié)果存于 FutureTask.outcome) 。
FutureTask還支持任務(wù)的取消功能,這一切都是通過(guò) FutureTask的state狀態(tài)來(lái)協(xié)調(diào)多個(gè)線程的。
總結(jié)
FutureTask接口是一種實(shí)現(xiàn)機(jī)制,提供我們對(duì)任務(wù)的執(zhí)行的跟蹤以及控制,相比于線程池本身,比較簡(jiǎn)單,相信不難理解。
以上就是本文關(guān)于Java線程池FutureTask實(shí)現(xiàn)原理詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
Java線程池ForkJoinPool(工作竊取算法)的使用
Fork就是把一個(gè)大任務(wù)切分為若干個(gè)子任務(wù)并行地執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。Fork/Join?框架使用的是工作竊取算法。本文主要介紹了ForkJoinPool的使用,需要的可以參考一下2022-11-11
Spring WebFlux實(shí)現(xiàn)參數(shù)校驗(yàn)的示例代碼
請(qǐng)求參數(shù)校驗(yàn),在實(shí)際的應(yīng)用中很常見(jiàn),網(wǎng)上的文章大部分提供的使用注解的方式做參數(shù)校驗(yàn)。本文主要介紹 Spring Webflux Function Endpoint 使用 Spring Validation 來(lái)校驗(yàn)請(qǐng)求的參數(shù)。感興趣的可以了解一下2021-08-08
Maven方式構(gòu)建SpringBoot項(xiàng)目的實(shí)現(xiàn)步驟(圖文)
Maven是一個(gè)強(qiáng)大的項(xiàng)目管理工具,可以幫助您輕松地構(gòu)建和管理Spring Boot應(yīng)用程序,本文主要介紹了Maven方式構(gòu)建SpringBoot項(xiàng)目的實(shí)現(xiàn)步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
java 交換兩個(gè)數(shù)據(jù)的方法實(shí)例詳解
這篇文章主要介紹了java 交換兩個(gè)數(shù)據(jù)的方法實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12
Java使用JDBC驅(qū)動(dòng)連接MySQL數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了Java使用JDBC驅(qū)動(dòng)連接MySQL數(shù)據(jù)庫(kù)的具體步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
使用Spring和Redis創(chuàng)建處理敏感數(shù)據(jù)的服務(wù)的示例代碼
許多公司處理的用戶敏感數(shù)據(jù)由于法律限制不能永久存儲(chǔ),根據(jù)規(guī)定,這些數(shù)據(jù)的存儲(chǔ)時(shí)間不能超過(guò)預(yù)設(shè)期限,并且最好在用于服務(wù)目的之后就將其刪除,解決這個(gè)問(wèn)題有多種可能的方案,在本文中,我想展示一個(gè)利用 Spring 和 Redis 處理敏感數(shù)據(jù)的應(yīng)用程序的簡(jiǎn)化示例2025-04-04
Mybatis-Plus中Mapper的接口文件與xml文件相關(guān)的坑記錄
這篇文章主要介紹了Mybatis-Plus中Mapper的接口文件與xml文件相關(guān)的坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

