真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java異步編程中如何進(jìn)行FutureTask源碼分析

本篇文章給大家分享的是有關(guān)Java異步編程中如何進(jìn)行FutureTask源碼分析,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

創(chuàng)新互聯(lián)公司專注于岐山網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供岐山營銷型網(wǎng)站建設(shè),岐山網(wǎng)站制作、岐山網(wǎng)頁設(shè)計、岐山網(wǎng)站官網(wǎng)定制、小程序設(shè)計服務(wù),打造岐山網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供岐山網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

Java的異步編程是一項非常常用的多線程技術(shù)。

但之前的任務(wù)主要傾向于線程池,并沒有講到異步編程方面的內(nèi)容。

萬事從示例開始,我們先通過示例Demo有一個直觀的印象,再深入去了解概念與原理。

使用示例

Java異步編程中如何進(jìn)行FutureTask源碼分析

使用上比較簡單,

運(yùn)行結(jié)果:

任務(wù)1異步執(zhí)行:0
任務(wù)2異步執(zhí)行:0
任務(wù)2異步執(zhí)行:1
...
任務(wù)2異步執(zhí)行:45
同步代碼
任務(wù)2異步執(zhí)行:24
...
任務(wù)1異步執(zhí)行:199
任務(wù)1:執(zhí)行完成
...
任務(wù)2異步執(zhí)行:199
任務(wù)2:執(zhí)行完成

假若你多次執(zhí)行這個程序,會發(fā)現(xiàn)結(jié)果大大的不一樣,因?yàn)閮蓚€任務(wù)和同步代碼是異步由多條線程執(zhí)行的,打印的結(jié)果當(dāng)然是隨機(jī)的。

回顧這個Demo做了什么,

  1. 構(gòu)建了一個線程池

  2. 往線程池里面丟兩個需要執(zhí)行的任務(wù)

  3. 最后獲取這兩個任務(wù)的結(jié)果

其中第二點(diǎn)是異步執(zhí)行兩個任務(wù),這兩個任務(wù)和主線程分別是用了三個線程并發(fā)執(zhí)行的,第三點(diǎn)是在主線程中同步等待兩個任務(wù)的結(jié)果。

很容易看出來,異步編程的好處就在于可以讓不相干的任務(wù)異步執(zhí)行,不阻塞主線程。若是主線程需要異步執(zhí)行的結(jié)果,此時再去等待結(jié)果會更加高效,提高程序的執(zhí)行效率。

下面來看看整個流程的實(shí)現(xiàn)原理。

源碼分析

一般在實(shí)際項目中,都會有配置有自己的線程池,建議大家在用異步編程時,配置一個專用的線程池,做好線程隔離,避免異步線程影響到其他模塊的工作。Demo中為了方便,直接調(diào)用Exectors的方法生成一個臨時的線程池,日常不建議使用。

我們從這個ExecutorService.submit()方法入手,看看整體實(shí)現(xiàn)。

Java異步編程中如何進(jìn)行FutureTask源碼分析

ExecutorService.submit()定義一個接口。這個接口接收一個Callable參數(shù)(執(zhí)行的任務(wù)),返回一個Future(計算結(jié)果)。

Callable,相當(dāng)于一個需要執(zhí)行的任務(wù)。它不接收任何參數(shù),可以返回結(jié)果,可以拋出異常。相類似的還有Runnable,它也是不接收,不同點(diǎn)在于它不返回結(jié)果,也不拋異常,異常需要在任務(wù)內(nèi)部處理。總結(jié)來說Callable更像一個方法的調(diào)用,Runnable則是一個不需要理會結(jié)果的調(diào)用。在JDK 8以后,它們都可以通過Lamda表達(dá)式寫法去替代內(nèi)部類的寫法(詳見Demo)。

Future,一個異步計算的結(jié)果。調(diào)用get()方法可以得到對應(yīng)的計算結(jié)果,如果調(diào)用時沒有異步計算完,會阻塞等待計算的結(jié)果。同時它還提供方法可以嘗試取消任務(wù)的執(zhí)行。

看回ExecutorService.submit()的實(shí)現(xiàn),代碼在實(shí)現(xiàn)類AbstractExecutorService中。

Java異步編程中如何進(jìn)行FutureTask源碼分析

除了它接口的實(shí)現(xiàn),還提供了兩種變形。原來接口只接收Callable參數(shù),實(shí)現(xiàn)類中還新增了接收Runnable參數(shù)的。

如果看過之前寫的《你真的懂ThreadPoolExecutor線程池技術(shù)嗎?看了源碼你會有全新的認(rèn)識》,應(yīng)該了解ThreadPoolExecutor執(zhí)行任務(wù)是可以調(diào)用execute()方法的。而這里面submit()方法則是為Callable/Runnable加多一層FutureTask,從而 使執(zhí)行結(jié)果有一個存放的地方,同時也添加一個可以取消的功能。原本的execute()只能執(zhí)行任務(wù),不會返回結(jié)果的,具體實(shí)現(xiàn)原理可以看看之前的文章分析。

FutureTaskRunnableFuture的實(shí)現(xiàn)。而RunnableFuture是繼承FutureRunnable接口的,定義run()接口。

Java異步編程中如何進(jìn)行FutureTask源碼分析

因?yàn)?code>FutureTask有run()接口,所以可以直接用一個Callable/Runnable創(chuàng)建一個FutureTask單獨(dú)執(zhí)行。但這樣并沒有異步的效果,因?yàn)闆]有啟用新的線程去跑,而是在原來的線程阻塞執(zhí)行的。

到這里我們清楚知道了,submit()方法重點(diǎn)是利用Callable/Runnable創(chuàng)建一個FutureTask,然后多線程執(zhí)行run()方法,達(dá)到異步處理并且得到結(jié)果的效果。而FutureTask的重點(diǎn)則是run()方法如何持有保存計算的結(jié)果。

FutureTask.run()

Java異步編程中如何進(jìn)行FutureTask源碼分析

首先判斷futureTask對象的state狀態(tài),如果不是NEW的話,證明已經(jīng)開始運(yùn)行過了,則退出執(zhí)行。同時futureTask對象通過CAS,把當(dāng)前線程賦值給變量runner(是Thread類型,說明對象使用哪個線程執(zhí)行的),如果CAS失敗則退出。

外層try{}代碼塊中,對callable判空和state狀態(tài)必須是NEW。內(nèi)層try{}代碼真正調(diào)用callable,開始執(zhí)行任務(wù)。若執(zhí)行成功,則把ran變量設(shè)為true,保存結(jié)果在result變量中,證明已跑成功過了;若拋異常了,則設(shè)為false,result為空,并且調(diào)用setException()保存異常。最后如果ran為true的話,則調(diào)用set()保存result結(jié)果。

看下setException()set()的實(shí)現(xiàn)。 Java異步編程中如何進(jìn)行FutureTask源碼分析

兩者的基本流程一樣,CAS置換狀態(tài),保存結(jié)果在outcome變量道中,但setException()保存的結(jié)果類型固定是Throwable。另外一個不同在于最終state狀態(tài),一個是EXCEPTION,一個是NORMAL。

這兩個方法最后都調(diào)用了finishCompletion()。這個方法主要是配合線程池喚醒下一個任務(wù)。

FutureTask.get()

從上面run()方法得知,最后執(zhí)行的結(jié)果放在了outcome變量中。那最終怎么從其中取出結(jié)果來,我們來看看get()方法。

Java異步編程中如何進(jìn)行FutureTask源碼分析

從源碼可知,get()方法分兩步。第一步,先判斷狀態(tài),如果計算為完成,則需要阻塞地等待完成。第二步,如果完成了,則調(diào)用report()方法獲取結(jié)果并返回。

先看看awaitDone()阻塞等待完成。該方法可以選用超時功能。

Java異步編程中如何進(jìn)行FutureTask源碼分析

在自旋的for()循環(huán)中,

  • 先判斷是否線程被中斷,中斷的話拋異常退出。

  • 然后開始判斷運(yùn)行的state值,如果state大于COMPLETING,證明計算已經(jīng)是終態(tài)了,此時返回終態(tài)變量。

  • state等于COMPLETING,證明已經(jīng)開始計算,并且還在計算中。此時為了避免過多的CPU時間放在這個for循環(huán)的自旋上,程序執(zhí)行Thread.yield(),把線程從運(yùn)行態(tài)降為就緒態(tài),讓出CPU時間。

  • 若以上狀態(tài)都不是,則證明stateNEW,還沒開始執(zhí)行。那么程序在當(dāng)前循環(huán)現(xiàn)在會新增一個WaitNode,在下一個循環(huán)里面調(diào)用 LockSupport.park()把當(dāng)前線程阻塞。當(dāng)run()方法結(jié)束的時候,會再次喚醒此線程,避免自旋消耗CPU時間。

  • 如果選用了超時功能,在阻塞和自旋過程中超時了,則會返回當(dāng)前超時的狀態(tài)。

第二步的report()方法比較簡單。

Java異步編程中如何進(jìn)行FutureTask源碼分析

  • 如果狀態(tài)是NORMAL,正常結(jié)束的話,則把outcome變量返回;

  • 如果是取消或者中斷狀態(tài)的,則拋出取消異常;

  • 如果是EXCEPTION,則把outcome當(dāng)作異常拋出(之前setException()保存的類型就是Throwable)。從而整個get()會有一個異常拋出。

至此我們已經(jīng)比較完整地了解Executor+Future的框架原理了,而FutureTask則是該框架的主要實(shí)現(xiàn)。下面總結(jié)下要點(diǎn)

  1. Executor.sumbit()方法異步執(zhí)行一個任務(wù),并且返回一個Future結(jié)果。

  2. submit()的原理是利用Callable創(chuàng)建一個FutureTask對象,然后執(zhí)行對象的run()方法,把結(jié)果保存在outcome中。

  3. 調(diào)用get()獲取outcome時,如果任務(wù)未完成,會阻塞線程,等待執(zhí)行完畢。

  4. 異常和正常結(jié)果都放在outcome中,調(diào)用get()獲取結(jié)果或拋出異常。

以上就是Java異步編程中如何進(jìn)行FutureTask源碼分析,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


網(wǎng)站名稱:Java異步編程中如何進(jìn)行FutureTask源碼分析
標(biāo)題URL:http://weahome.cn/article/jgsosp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部