国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java并發(fā)編程78講--第01講:為何說只有 1 種實現(xiàn)線程的方法

2023-03-02 09:05 作者:gzqhero  | 我要投稿

在本課時我們主要學(xué)習(xí)為什么說本質(zhì)上只有一種實現(xiàn)線程的方式?實現(xiàn) Runnable 接口究竟比繼承 Thread 類實現(xiàn)線程好在哪里?

實現(xiàn)線程是并發(fā)編程中基礎(chǔ)中的基礎(chǔ),因為我們必須要先實現(xiàn)多線程,才可以繼續(xù)后續(xù)的一系列操作。所以本課時就先從并發(fā)編程的基礎(chǔ)如何實現(xiàn)線程開始講起,希望你能夠夯實基礎(chǔ),雖然實現(xiàn)線程看似簡單、基礎(chǔ),但實際上卻暗藏玄機。首先,我們來看下為什么說本質(zhì)上實現(xiàn)線程只有一種方式?

實現(xiàn)線程的方式到底有幾種?大部分人會說有 2 種、3 種或是 4 種,很少有人會說有 1 種。我們接下來看看它們具體指什么?2 種實現(xiàn)方式的描述是最基本的,也是最為大家熟知的,我們就先來看看 2 種線程實現(xiàn)方式的源碼。

實現(xiàn) Runnable 接口

第 1 種方式是通過實現(xiàn) Runnable 接口實現(xiàn)多線程,如代碼所示,首先通過 RunnableThread 類實現(xiàn) Runnable 接口,然后重寫 run() 方法,之后只需要把這個實現(xiàn)了 run() 方法的實例傳到 Thread 類中就可以實現(xiàn)多線程。

繼承 Thread 類

第 2 種方式是繼承 Thread 類,如代碼所示,與第 1 種方式不同的是它沒有實現(xiàn)接口,而是繼承 Thread 類,并重寫了其中的 run() 方法。相信上面這兩種方式你一定非常熟悉,并且經(jīng)常在工作中使用它們。

線程池創(chuàng)建線程

那么為什么說還有第 3 種或第 4 種方式呢?我們先來看看第 3 種方式:通過線程池創(chuàng)建線程。線程池確實實現(xiàn)了多線程,比如我們給線程池的線程數(shù)量設(shè)置成 10,那么就會有 10 個子線程來為我們工作,接下來,我們深入解析線程池中的源碼,來看看線程池是怎么實現(xiàn)線程的?

對于線程池而言,本質(zhì)上是通過線程工廠創(chuàng)建線程的,默認采用 DefaultThreadFactory ,它會給線程池創(chuàng)建的線程設(shè)置一些默認值,比如:線程的名字、是否是守護線程,以及線程的優(yōu)先級等。但是無論怎么設(shè)置這些屬性,最終它還是通過 new?Thread() 創(chuàng)建線程的 ,只不過這里的構(gòu)造函數(shù)傳入的參數(shù)要多一些,由此可以看出通過線程池創(chuàng)建線程并沒有脫離最開始的那兩種基本的創(chuàng)建方式,因為本質(zhì)上還是通過 new?Thread() 實現(xiàn)的。

在面試中,如果你只是知道這種方式可以創(chuàng)建線程但不了解其背后的實現(xiàn)原理,就會在面試的過程中舉步維艱,想更好的表現(xiàn)自己卻給自己挖了“坑”。

所以我們在回答線程實現(xiàn)的問題時,描述完前兩種方式,可以進一步引申說“我還知道線程池和Callable 也是可以創(chuàng)建線程的,但是它們本質(zhì)上也是通過前兩種基本方式實現(xiàn)的線程創(chuàng)建?!边@樣的回答會成為面試中的加分項。然后面試官大概率會追問線程池的構(gòu)成及原理,這部分內(nèi)容會在后面的課時中詳細分析。

有返回值的 Callable 創(chuàng)建線程

第 4 種線程創(chuàng)建方式是通過有返回值的 Callable 創(chuàng)建線程,Runnable 創(chuàng)建線程是無返回值的,而 Callable 和與之相關(guān)的 Future、FutureTask,它們可以把線程執(zhí)行的結(jié)果作為返回值返回,如代碼所示,實現(xiàn)了 Callable 接口,并且給它的泛型設(shè)置成 Integer,然后它會返回一個隨機數(shù)。

但是,無論是 Callable 還是 FutureTask,它們首先和 Runnable 一樣,都是一個任務(wù),是需要被執(zhí)行的,而不是說它們本身就是線程。它們可以放到線程池中執(zhí)行,如代碼所示, submit() 方法把任務(wù)放到線程池中,并由線程池創(chuàng)建線程,不管用什么方法,最終都是靠線程來執(zhí)行的,而子線程的創(chuàng)建方式仍脫離不了最開始講的兩種基本方式,也就是實現(xiàn) Runnable 接口和繼承 Thread 類。

其他創(chuàng)建方式

定時器 Timer

講到這里你可能會說,我還知道一些其他的實現(xiàn)線程的方式。比如,定時器也可以實現(xiàn)線程,如果新建一個 Timer,令其每隔 10 秒或設(shè)置兩個小時之后,執(zhí)行一些任務(wù),那么這時它確實也創(chuàng)建了線程并執(zhí)行了任務(wù),但如果我們深入分析定時器的源碼會發(fā)現(xiàn),本質(zhì)上它還是會有一個繼承自 Thread 類的?TimerThread,所以定時器創(chuàng)建線程最后又繞回到最開始說的兩種方式。

其他方法

或許你還會說,我還知道一些其他方式,比如匿名內(nèi)部類或 lambda 表達式方式,實際上,匿名內(nèi)部類或 lambda 表達式創(chuàng)建線程,它們僅僅是在語法層面上實現(xiàn)了線程,并不能把它歸結(jié)于實現(xiàn)多線程的方式,如匿名內(nèi)部類實現(xiàn)線程的代碼所示,它僅僅是用一個匿名內(nèi)部類把需要傳入的 Runnable 給實例出來。

我們再來看下 lambda 表達式方式。如代碼所示,最終它們依然符合最開始所說的那兩種實現(xiàn)線程的方式。

實現(xiàn)線程只有一種方式

關(guān)于這個問題,我們先不聚焦為什么說創(chuàng)建線程只有一種方式,先認為有兩種創(chuàng)建線程的方式,而其他的創(chuàng)建方式,比如線程池或是定時器,它們僅僅是在 new?Thread()?外做了一層封裝,如果我們把這些都叫作一種新的方式,那么創(chuàng)建線程的方式便會千變?nèi)f化、層出不窮,比如 JDK 更新了,它可能會多出幾個類,會把 new?Thread()?重新封裝,表面上看又會是一種新的實現(xiàn)線程的方式,透過現(xiàn)象看本質(zhì),打開封裝后,會發(fā)現(xiàn)它們最終都是基于 Runnable 接口或繼承 Thread 類實現(xiàn)的。

接下來,我們進行更深層次的探討,為什么說這兩種方式本質(zhì)上是一種呢?

首先,啟動線程需要調(diào)用 start() 方法,而 start() 方法最終還會調(diào)用 run() 方法,我們先來看看第一種方式中 run() 方法究竟是怎么實現(xiàn)的,可以看出 run() 方法的代碼非常短小精悍,第 1 行代碼 if (target != null) ,判斷 target 是否等于 null,如果不等于 null,就執(zhí)行第 2 行代碼 target.run(),而 target?實際上就是一個 Runnable,即使用 Runnable 接口實現(xiàn)線程時傳給Thread類的對象。

然后,我們來看第二種方式,也就是繼承 Thread 方式,實際上,繼承 Thread 類之后,會把上述的 run() 方法重寫,重寫后 run() 方法里直接就是所需要執(zhí)行的任務(wù),但它最終還是需要調(diào)用 thread.start() 方法來啟動線程,而 start() 方法最終也會調(diào)用這個已經(jīng)被重寫的?run() 方法來執(zhí)行它的任務(wù),這時我們就可以徹底明白了,事實上創(chuàng)建線程只有一種方式,就是構(gòu)造一個 Thread 類,這是創(chuàng)建線程的唯一方式。

我們上面已經(jīng)了解了兩種創(chuàng)建線程方式本質(zhì)上是一樣的,它們的不同點僅僅在于 實現(xiàn)線程運行內(nèi)容的不同,那么運行內(nèi)容來自于哪里呢?

運行內(nèi)容主要來自于兩個地方,要么來自于 target,要么來自于重寫的 run() 方法,在此基礎(chǔ)上我們進行拓展,可以這樣描述:本質(zhì)上,實現(xiàn)線程只有一種方式,而要想實現(xiàn)線程執(zhí)行的內(nèi)容,卻有兩種方式,也就是可以通過?實現(xiàn) Runnable 接口的方式,或是繼承 Thread 類重寫 run() 方法的方式,把我們想要執(zhí)行的代碼傳入,讓線程去執(zhí)行,在此基礎(chǔ)上,如果我們還想有更多實現(xiàn)線程的方式,比如線程池和 Timer 定時器,只需要在此基礎(chǔ)上進行封裝即可。

實現(xiàn) Runnable 接口比繼承 Thread 類實現(xiàn)線程要好

下面我們來對剛才說的兩種實現(xiàn)線程內(nèi)容的方式進行對比,也就是為什么說實現(xiàn) Runnable 接口比繼承 Thread 類實現(xiàn)線程要好?好在哪里呢?

首先,我們從代碼的架構(gòu)考慮,實際上,Runnable 里只有一個 run() 方法,它定義了需要執(zhí)行的內(nèi)容,在這種情況下,實現(xiàn)了 Runnable 與 Thread 類的解耦,Thread 類負責(zé)線程啟動和屬性設(shè)置等內(nèi)容,權(quán)責(zé)分明。

第二點就是在某些情況下可以提高性能,使用繼承 Thread 類方式,每次執(zhí)行一次任務(wù),都需要新建一個獨立的線程,執(zhí)行完任務(wù)后線程走到生命周期的盡頭被銷毀,如果還想執(zhí)行這個任務(wù),就必須再新建一個繼承了 Thread 類的類,如果此時執(zhí)行的內(nèi)容比較少,比如只是在 run() 方法里簡單打印一行文字,那么它所帶來的開銷并不大,相比于整個線程從開始創(chuàng)建到執(zhí)行完畢被銷毀,這一系列的操作比 run() 方法打印文字本身帶來的開銷要大得多,相當于撿了芝麻丟了西瓜,得不償失。如果我們使用實現(xiàn) Runnable 接口的方式,就可以把任務(wù)直接傳入線程池,使用一些固定的線程來完成任務(wù),不需要每次新建銷毀線程,大大降低了性能開銷。

第三點好處在于 Java 語言不支持雙繼承,如果我們的類一旦繼承了 Thread 類,那么它后續(xù)就沒有辦法再繼承其他的類,這樣一來,如果未來這個類需要繼承其他類實現(xiàn)一些功能上的拓展,它就沒有辦法做到了,相當于限制了代碼未來的可拓展性。

綜上所述,我們應(yīng)該優(yōu)先選擇通過實現(xiàn) Runnable 接口的方式來創(chuàng)建線程。

好啦,本課時的全部內(nèi)容就講完了,在這一課時我們主要學(xué)習(xí)了 通過 Runnable 接口和繼承 Thread 類等幾種方式創(chuàng)建線程,又詳細分析了為什么說本質(zhì)上只有一種實現(xiàn)線程的方式,以及實現(xiàn) Runnable 接口究竟比繼承 Thread 類實現(xiàn)線程好在哪里?學(xué)習(xí)完本課時相信你一定對創(chuàng)建線程有了更深入的理解。


Java并發(fā)編程78講--第01講:為何說只有 1 種實現(xiàn)線程的方法的評論 (共 條)

分享到微博請遵守國家法律
南阳市| 蓬溪县| 六枝特区| 米泉市| 安康市| 宜宾县| 高要市| 阿拉尔市| 威海市| 南宫市| 安泽县| 西丰县| 绥滨县| 津南区| 称多县| 海盐县| 彰化市| 巴青县| 陵水| 广州市| 宁波市| 五河县| 富顺县| 姜堰市| 灯塔市| 额济纳旗| 抚顺市| 遂平县| 正镶白旗| 汤阴县| 朝阳区| 皮山县| 老河口市| 旌德县| 邛崃市| 大冶市| 东源县| 福清市| 澄江县| 拜泉县| 思南县|