線程和線程池

多線程編程是業(yè)務(wù)開發(fā)中要用到的一項技術(shù),盡管面臨著一些挑戰(zhàn),但多線程開發(fā)也有著很多優(yōu)點,例如資源利用率更好,程序設(shè)計在某些情況下更簡單,程序響應更快等等。接下來主要說一下線程的幾種創(chuàng)建方式以及線程池的應用。

1通過繼承Thread的方式來創(chuàng)建線程


2通過實現(xiàn)Runnable接口來創(chuàng)建線程


這兩種方式本質(zhì)上其實是一樣的,都是通過run()方法,深挖源碼的話,第一種方式中Thread其實也是實現(xiàn)了Runnable接口,然后重寫run()方法。然而這兩種方式有一個不足,就是run()方法里并不能返回一個結(jié)果和拋出異常,所以就有了第三種創(chuàng)建線程的方式,通過Callable接口,并用FutureTask來接收返回的對象。

3通過Callable接口,并用FutureTask來接收返回的對象


和Runnable接口不一樣,Callable接口提供了一個call()方法作為線程執(zhí)行體,call()方法比run()方法功能要強大。call()方法可以有返回值,call()方法可以聲明拋出異常

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

接下來說線程池,線程池是管理線程的地方,在一個項目中,我們知道需要多次的創(chuàng)建線程和銷毀線程,然而創(chuàng)建和銷毀是及其耗費內(nèi)存資源的,所以就有了線程池的需要,來幫我們管理調(diào)度線程,省下了很多的內(nèi)存資源。

我們應該如何創(chuàng)建一個線程池那?Java中已經(jīng)提供了創(chuàng)建線程池的一個類:Executor

而我們創(chuàng)建時,一般使用它的子類:ThreadPoolExecutor。但是從ThreadPoolExecutor到Executor并不是簡單的實現(xiàn)或繼承關(guān)系,兩者中間有好多中間類。下面是各類之間的層級關(guān)系調(diào)用實現(xiàn)類圖


線程池類圖

我把這幾個類用黑色線條畫了起來,發(fā)現(xiàn)我們需要的ThreadPoolExecutor首先繼承了抽象類AbstractExecutorService,然后AbstractExecutorService實現(xiàn)了ExecutorService接口,最ExecutorService接口又實現(xiàn)了Executor接口。

我們再來看一下ThreadPoolExecutor構(gòu)造函數(shù)的參數(shù)


我們可以看出,線程池中的corePoolSize就是線程池中的核心線程數(shù)量,這幾個核心線程,只是在沒有用的時候,也不會被回收,maximumPoolSize就是線程池中可以容納的最大線程的數(shù)量,而keepAliveTime,就是線程池中除了核心線程之外的其他的最長可以保留的時間,因為在線程池中,除了核心線程即使在無任務(wù)的情況下也不能被清除,其余的都是有存活時間的,意思就是非核心線程可以保留的最長的空閑時間,而util,就是計算這個時間的一個單位,workQueue,就是等待隊列,任務(wù)可以儲存在任務(wù)隊列中等待被執(zhí)行,執(zhí)行的是FIFIO原則(先進先出)。threadFactory,就是創(chuàng)建線程的線程工廠,最后一個handler,是一種拒絕策略,我們可以在任務(wù)滿了之后,拒絕執(zhí)行某些任務(wù),具體的拒絕策略如下:

ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復此過程)

ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)

再來看線程池的執(zhí)行流程


線程池的執(zhí)行流程

我們可以看出,任務(wù)進來時,首先執(zhí)行判斷,判斷核心線程是否處于空閑狀態(tài),如果不是,核心線程就先就執(zhí)行任務(wù),如果核心線程已滿,則判斷任務(wù)隊列是否有地方存放該任務(wù),若果有,就將任務(wù)保存在任務(wù)隊列中,等待執(zhí)行,如果滿了,在判斷最大可容納的線程數(shù),如果沒有超出這個數(shù)量,就開創(chuàng)非核心線程執(zhí)行任務(wù),如果超出了,就調(diào)用handler實現(xiàn)拒絕策略。

下面一段測試代碼來說明這個線程池執(zhí)行的流程


最后運行結(jié)果與執(zhí)行流程是一樣的,都是先判斷核心線程,當核心線程最大后,在判斷隊列,當隊列也滿后,在進入非核心線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容