引言
FutureTask實(shí)現(xiàn)了接口Future,同F(xiàn)uture一樣,代表異步計(jì)算的結(jié)果泉沾。當(dāng)然,F(xiàn)utureTask除了實(shí)現(xiàn)Future接口之外妇押,還實(shí)現(xiàn)了Runnable接口跷究,所以,F(xiàn)utureTask既可以由Executor來(lái)調(diào)度執(zhí)行敲霍,也可以由調(diào)度線程調(diào)用FutureTask.run()直接執(zhí)行俊马。
FutureTask狀態(tài)
根據(jù)FutureTask的run方法是否被執(zhí)行以及是否被執(zhí)行完成,F(xiàn)utureTask有3種狀態(tài):
未啟動(dòng):run方法被執(zhí)行前肩杈,F(xiàn)utureTask處于未啟動(dòng)狀態(tài)柴我;
已啟動(dòng):run方法被執(zhí)行的過(guò)程中,F(xiàn)utureTask處于已啟動(dòng)狀態(tài)扩然;
已完成:run方法執(zhí)行完成后正常結(jié)束艘儒,或者被取消,或者是執(zhí)行過(guò)程中拋出異常導(dǎo)致的異常結(jié)束,F(xiàn)utureTask處于已完成狀態(tài)彤悔。
FutureTask狀態(tài)轉(zhuǎn)換
FutureTask狀態(tài)轉(zhuǎn)換可以總結(jié)為下圖:
當(dāng)FutureTask處于未啟動(dòng)或者是已啟動(dòng)狀態(tài)時(shí)嘉抓,此時(shí)還未得到線程執(zhí)行結(jié)果,調(diào)用FutureTask.get方法會(huì)導(dǎo)致線程阻塞晕窑;
當(dāng)FutureTask處于已完成狀態(tài)時(shí)抑片,此時(shí)已經(jīng)得到線程執(zhí)行結(jié)果,調(diào)用FutureTask.get方法會(huì)立即返回線程執(zhí)行結(jié)果杨赤;
當(dāng)FutureTask處于未啟動(dòng)狀態(tài)時(shí)敞斋,調(diào)用FutureTask.cancel方法將會(huì)導(dǎo)致該task永遠(yuǎn)不會(huì)被執(zhí)行;
當(dāng)FutureTask處于啟動(dòng)狀態(tài)時(shí)疾牲,調(diào)用FutureTask.cancel方法將會(huì)中斷該任務(wù)的執(zhí)行植捎,至于會(huì)不會(huì)對(duì)任務(wù)產(chǎn)生影響由cancel方法的入?yún)Q定;
當(dāng)FutureTask處于已完成狀態(tài)阳柔,調(diào)用FutureTask.cancel方法返回false焰枢。
接下來(lái)就以run、get和cancel方法為切入點(diǎn)分析FutureTask具體實(shí)現(xiàn)舌剂。
FutureTask源碼分析
在開(kāi)始分析源碼之前济锄,我們先來(lái)看看FutureTask的成員變量:
state:記錄task狀態(tài),可取值為0~6霍转;
callable:task實(shí)際載體荐绝,run方法實(shí)際調(diào)用callable.call();
outcome:線程執(zhí)行任務(wù)結(jié)束后的返回結(jié)果避消;
runner:記錄執(zhí)行task的線程低滩;
waiters:等待task執(zhí)行結(jié)果的線程隊(duì)列。
構(gòu)造方法
FutureTask提供兩個(gè)構(gòu)造方法來(lái)封裝Callable和Runnable岩喷,當(dāng)構(gòu)造方法傳入?yún)?shù)為Runnable恕沫,會(huì)通過(guò)Executors.callable方法將其轉(zhuǎn)換成Callable。
get方法實(shí)現(xiàn)
FutureTask提供帶超時(shí)時(shí)間的get和不到超時(shí)時(shí)間的get:
對(duì)比帶超時(shí)時(shí)間和不帶超時(shí)時(shí)間的get方法實(shí)現(xiàn)纱意,最為重要的實(shí)現(xiàn)就是等待直到task狀態(tài)變?yōu)橐淹瓿蔂顟B(tài)或者等待時(shí)間超過(guò)超時(shí)時(shí)間昏兆,對(duì)應(yīng)到源碼就是加紅框的awaitDone方法。接下來(lái)我們來(lái)具體分析一下awaitDone方法到底是如何來(lái)實(shí)現(xiàn)線程阻塞等待的妇穴。
awaitDone方法實(shí)現(xiàn)
具體的執(zhí)行流程如下:
計(jì)算等待時(shí)間deadline,如果是帶超時(shí)時(shí)間的get隶债,deadline = 當(dāng)前時(shí)間 + 等待時(shí)間腾它,如果是不帶超時(shí)時(shí)間的get,deadline = 0死讹;
判斷線程是否中斷瞒滴,如果線程中斷,將當(dāng)前線程從等待隊(duì)列waiters中移除,拋出中斷異常妓忍,否則虏两,跳轉(zhuǎn)到步驟3;
獲取task狀態(tài)state:
如果task狀態(tài)為已完成狀態(tài)世剖,將等待線程節(jié)點(diǎn)的線程置為null定罢,返回state;
如果task狀態(tài)為正在執(zhí)行旁瘫,調(diào)用Thread.yield()將線程從執(zhí)行狀態(tài)變?yōu)榭蓤?zhí)行狀態(tài)祖凫;
否則,跳轉(zhuǎn)到步驟4酬凳;
如果等待線程節(jié)點(diǎn)q為null惠况,初始化等待線程節(jié)點(diǎn)q,否則宁仔,跳轉(zhuǎn)到步驟5稠屠;
如果當(dāng)前等待線程節(jié)點(diǎn)q還未成功進(jìn)入等待隊(duì)列waiters,進(jìn)入線程等待隊(duì)列翎苫,否則权埠,跳轉(zhuǎn)到步驟6;
判斷是否是帶超時(shí)時(shí)間的get:
如果是帶超時(shí)時(shí)間get拉队,判斷當(dāng)前是否超時(shí)弊知,如果已經(jīng)超時(shí),將當(dāng)前等待節(jié)點(diǎn)q從waiters中移出粱快,返回task狀態(tài)state秩彤,如果還未超時(shí),調(diào)用LockSupport.parkNanos方法阻塞當(dāng)前線程事哭;
否則漫雷,跳轉(zhuǎn)到步驟7;
- 調(diào)用LockSupport.park方法鳍咱,阻塞當(dāng)前線程降盹,然后跳轉(zhuǎn)到步驟2。
從get方法整個(gè)流程可以看出:
FutureTask維護(hù)一個(gè)等待線程隊(duì)列waiters谤辜,如果task還未執(zhí)行完畢蓄坏,調(diào)用get方法的線程會(huì)先進(jìn)入等待隊(duì)列自旋等待;
awaitDone方法其實(shí)是個(gè)死循環(huán)丑念,直到task狀態(tài)變?yōu)橐淹瓿蔂顟B(tài)或者等待時(shí)間超過(guò)超時(shí)時(shí)間或者線程中斷才會(huì)跳出循環(huán)涡戳,程序結(jié)束;
為了節(jié)省開(kāi)銷脯倚,線程不會(huì)一直自旋等待渔彰,而是會(huì)阻塞嵌屎,使用LockSupport的park系列方法實(shí)現(xiàn)線程阻塞;
run方法實(shí)現(xiàn)
具體執(zhí)行流程如下:
判斷task狀態(tài)恍涂,如果task還未執(zhí)行宝惰,跳轉(zhuǎn)到步驟2,否則再沧,返回尼夺,程序結(jié)束;
通過(guò)CAS設(shè)置執(zhí)行task的線程产园,設(shè)置成功汞斧,跳轉(zhuǎn)到步驟3,否則什燕,返回粘勒,程序結(jié)束;
執(zhí)行callable.call方法屎即,調(diào)用set方法設(shè)置call方法返回結(jié)果以及task狀態(tài)庙睡;
設(shè)置當(dāng)前運(yùn)行當(dāng)前task的線程為null;
判斷當(dāng)前task狀態(tài)技俐,如果task狀態(tài)為正在中斷或者已中斷乘陪,調(diào)用Thread.yield()將線程從執(zhí)行狀態(tài)變?yōu)榭蓤?zhí)行狀態(tài)。
set方法實(shí)現(xiàn)
set方法主要干了這兩件事:
設(shè)置返回結(jié)果outcome以及task狀態(tài)state雕擂;
調(diào)用finishCompletion方法操作等待隊(duì)列waiters中的等待線程啡邑。
finishCompletion實(shí)現(xiàn)
整個(gè)finishCompletion方法清除和喚醒了等待隊(duì)列中的等待線程,調(diào)用get方法被阻塞的線程也就是在這里調(diào)用LockSupport.unpark方法被喚醒的井赌。
cancel方法實(shí)現(xiàn)
判斷task狀態(tài)谤逼,如果不為未啟動(dòng)狀態(tài),返回false仇穗,程序結(jié)束流部,否則,跳轉(zhuǎn)到步驟2纹坐;
判斷入?yún)ayInterruptIfRunning:
true:CSA設(shè)置state為正在中斷枝冀,設(shè)置失敗返回false,否則中斷正在運(yùn)行task的線程耘子,CAS設(shè)置state為已中斷果漾;
false:CSA設(shè)置state為已取消,設(shè)置失敗返回false谷誓,需要注意的是跨晴,正在運(yùn)行task的線程是不會(huì)中斷的,換句話說(shuō)片林,入?yún)閒alse時(shí)不會(huì)對(duì)task的執(zhí)行有任何影響。
注:根據(jù)代碼實(shí)現(xiàn):
- 處于啟動(dòng)狀態(tài)的task,調(diào)用cancel方法是否會(huì)對(duì)task的執(zhí)行有所影響完全依賴于cancel方法的入?yún)⒎逊猓瑃rue時(shí)會(huì)有影響焕妙,false時(shí)不會(huì)有影響;
- 處于未啟動(dòng)狀態(tài)的task弓摘,調(diào)用cancel方法后焚鹊,該task將不會(huì)再被執(zhí)行。
- 調(diào)用finishCompletion方法清除和喚醒等待隊(duì)列waiters中的等待線程韧献,返回true末患,程序結(jié)束。
從get锤窑、run璧针、cancel方法的實(shí)現(xiàn),F(xiàn)utureTask的線程等待與喚醒可以總結(jié)為下圖:
后記
到這里為止渊啰,F(xiàn)utureTask的源碼就分析就結(jié)束了探橱。做一個(gè)簡(jiǎn)短的總結(jié):
FutureTask是通過(guò)LockSupport來(lái)阻塞線程、喚醒線程绘证;
對(duì)于多線程訪問(wèn)成員變量waiters隧膏、state,都采用CAS來(lái)操作嚷那;
總的來(lái)說(shuō)胞枕,F(xiàn)utureTask是一個(gè)非常好的CAS和LockSupport搭配使用的例子。