【Java多線程系列】實(shí)現(xiàn)與應(yīng)用(1)創(chuàng)建線程的幾種方式

本篇屬于【Java多線程系列】文章第二章【多線程編程的實(shí)現(xiàn)與應(yīng)用】的第一小節(jié)內(nèi)容,我們會(huì)來(lái)學(xué)習(xí)創(chuàng)建線程的幾種方式,并對(duì)這幾種方式進(jìn)行比較。

繼承Thread類

通過(guò)繼承Thread類來(lái)創(chuàng)建并啟動(dòng)多線程的一般步驟如下:

(1)定義Thread類的子類,并重寫該類的run()方法,該方法的方法體就是線程需要完成的任務(wù),run()方法也稱為線程執(zhí)行體。

(2)創(chuàng)建Thread子類的實(shí)例,也就是創(chuàng)建了線程對(duì)象

(3)啟動(dòng)線程,即調(diào)用線程的start()方法

實(shí)現(xiàn)Runnable接口

通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建并啟動(dòng)線程一般步驟如下:

(1)定義Runnable接口的實(shí)現(xiàn)類,一樣要重寫run()方法,這個(gè)run()方法和Thread中的run()方法一樣是線程的執(zhí)行體

(2)創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并用這個(gè)實(shí)例作為Thread的target來(lái)創(chuàng)建Thread對(duì)象,這個(gè)Thread對(duì)象才是真正的線程對(duì)象

(3)第三部依然是通過(guò)調(diào)用線程對(duì)象的start()方法來(lái)啟動(dòng)線程

實(shí)現(xiàn)Callable接口

和Runnable接口不一樣,Callable接口提供了一個(gè)call()方法作為線程執(zhí)行體,call()方法比run()方法功能要強(qiáng)大:

(1)call方法可以有返回值

(2)call方法可以聲明拋出異常

Java5提供了Future接口來(lái)代表Callable接口里call方法的返回值,并且為Future接口提供了一個(gè)實(shí)現(xiàn)類FutureTask,這個(gè)實(shí)現(xiàn)類既實(shí)現(xiàn)了Future接口,還實(shí)現(xiàn)了Runnable接口,因此可以作為Thread類的target。在Future接口里定義了幾個(gè)公共方法來(lái)控制它關(guān)聯(lián)的Callable任務(wù)。

介紹了相關(guān)的概念之后,創(chuàng)建并啟動(dòng)有返回值的線程的步驟如下:

(1)創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)call()方法,然后創(chuàng)建該實(shí)現(xiàn)類的實(shí)例(從java8開(kāi)始可以直接使用Lambda表達(dá)式創(chuàng)建Callable對(duì)象)。

(2)使用FutureTask類來(lái)包裝Callable對(duì)象,該FutureTask對(duì)象封裝了Callable對(duì)象的call()方法的返回值

(3)使用FutureTask對(duì)象作為Thread對(duì)象的target創(chuàng)建并啟動(dòng)線程(因?yàn)镕utureTask實(shí)現(xiàn)了Runnable接口)

(4)調(diào)用FutureTask對(duì)象的get()方法來(lái)獲得子線程執(zhí)行結(jié)束后的返回值

使用線程池框架

1.5后引入的Executor框架的最大優(yōu)點(diǎn)是把任務(wù)的提交和執(zhí)行解耦。要執(zhí)行任務(wù)的人只需把Task描述清楚,然后提交即可。這個(gè)Task是怎么被執(zhí)行的,被誰(shuí)執(zhí)行的,什么時(shí)候執(zhí)行的,提交的人就不用關(guān)心了。

具體點(diǎn)講,提交一個(gè)Callable對(duì)象給ExecutorService(如最常用的線程池ThreadPoolExecutor),將得到一個(gè)Future對(duì)象,調(diào)用Future對(duì)象的get方法等待執(zhí)行結(jié)果就好了。

Executor框架的內(nèi)部使用了線程池機(jī)制,它在java.util.cocurrent 包下,通過(guò)該框架來(lái)控制線程的啟動(dòng)、執(zhí)行和關(guān)閉,可以簡(jiǎn)化并發(fā)編程的操作。因此,在Java 5之后,通過(guò)Executor來(lái)啟動(dòng)線程比使用Thread的start方法更好。

除了更易管理,效率更好(用線程池實(shí)現(xiàn),節(jié)約開(kāi)銷)外,還有關(guān)鍵的一點(diǎn):有助于避免this逃逸問(wèn)題——如果我們?cè)跇?gòu)造器中啟動(dòng)一個(gè)線程,因?yàn)榱硪粋€(gè)任務(wù)可能會(huì)在構(gòu)造器結(jié)束之前開(kāi)始執(zhí)行,此時(shí)可能會(huì)訪問(wèn)到初始化了一半的對(duì)象用Executor在構(gòu)造器中。

具體的相關(guān)內(nèi)容我們將在線程池章節(jié)中進(jìn)行詳細(xì)講解。

本文系【程序因子】版權(quán)作品,未經(jīng)授權(quán)嚴(yán)禁轉(zhuǎn)載,同時(shí)也歡關(guān)注同名公眾號(hào)【程序因子】迎投稿及合作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。