線程池理念分析及其手寫
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達(dá)
? 作者?|? 彼岸舞
來源 |? urlify.cn/IjyQzq
76套java從入門到精通實(shí)戰(zhàn)課程分享
什么是線程池?
線程池,thread pool,是一種線程使用模式
為什么要使用線程池?
1:降低資源的消耗,降低線程的創(chuàng)建和銷毀的資源消耗
2:提高響應(yīng)速度,假設(shè)線程的創(chuàng)建時間為T1,執(zhí)行時間為T2,銷毀時間為T3,如果是自己創(chuàng)建線程必然會經(jīng)歷,這三個時間,那么如果創(chuàng)建+銷毀>執(zhí)行,就會有大量時間用在創(chuàng)建和銷毀上,而不是在執(zhí)行任務(wù)上,而線程池關(guān)注的就是調(diào)整T1和T3的時間,線程池可以免去T1和T3的時間
3:提高線程的可管理性
不如先來實(shí)現(xiàn)一個自己的線程池
思想:
1:為了優(yōu)化T1和T3的時間,在使用前,線程必須在池中已經(jīng)創(chuàng)建好了,并且可以保持住,既然要保存住,那么就需要一個容器
2:里面的線程如果是只能跑已經(jīng)在內(nèi)部寫好的代碼,那么就沒有意義,所以它必須能接收外部的任務(wù),運(yùn)行這個任務(wù)
3:外部傳入的任務(wù)數(shù)量大于線程的執(zhí)行個數(shù)是,多余的任務(wù)如何處理?emmm,用個容器存起來,用阻塞隊(duì)列吧,上一章剛寫了
實(shí)現(xiàn)代碼:
package?com.xiangxue.ch6.mypool;
import?java.util.LinkedList;
import?java.util.List;
import?java.util.concurrent.ArrayBlockingQueue;
import?java.util.concurrent.BlockingQueue;
/**
?*?類說明:自己線程池的實(shí)現(xiàn)
?*/
public?class?MyThreadPool2?{
????//?線程池中默認(rèn)線程的個數(shù)為5
????private?static?int?WORK_NUM?=?5;
????
????//?隊(duì)列默認(rèn)任務(wù)個數(shù)為100
????private?static?int?TASK_COUNT?=?100;
????//?工作線程組
????private?WorkThread[]?workThreads;
????//?任務(wù)隊(duì)列,作為一個緩沖
????private?final?BlockingQueue?taskQueue;
????private?final?int?worker_num;//用戶在構(gòu)造這個池,希望的啟動的線程數(shù)
????//?創(chuàng)建具有默認(rèn)線程個數(shù)的線程池
????public?MyThreadPool2()?{
????????this(WORK_NUM,?TASK_COUNT);
????}
????//?創(chuàng)建線程池,worker_num為線程池中工作線程的個數(shù)
????public?MyThreadPool2(int?worker_num,?int?taskCount)?{
????????if?(worker_num?<=?0)?worker_num?=?WORK_NUM;
????????if?(taskCount?<=?0)?taskCount?=?TASK_COUNT;
????????this.worker_num?=?worker_num;
????????taskQueue?=?new?ArrayBlockingQueue<>(taskCount);
????????workThreads?=?new?WorkThread[worker_num];
????????for?(int?i?=?0;?i?????????????workThreads[i]?=?new?WorkThread();
????????????workThreads[i].start();
????????}
????????Runtime.getRuntime().availableProcessors();
????}
????//?執(zhí)行任務(wù),其實(shí)只是把任務(wù)加入任務(wù)隊(duì)列,什么時候執(zhí)行有線程池管理器決定
????public?void?execute(Runnable?task)?{
????????try?{
????????????taskQueue.put(task);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????}
????//?銷毀線程池,該方法保證在所有任務(wù)都完成的情況下才銷毀所有線程,否則等待任務(wù)完成才銷毀
????public?void?destroy()?{
????????//?工作線程停止工作,且置為null
????????System.out.println("ready?close?pool.....");
????????for?(int?i?=?0;?i?????????????workThreads[i].stopWorker();
????????????workThreads[i]?=?null;//help?gc
????????}
????????taskQueue.clear();//?清空任務(wù)隊(duì)列
????}
????//?覆蓋toString方法,返回線程池信息:工作線程個數(shù)和已完成任務(wù)個數(shù)
????@Override
????public?String?toString()?{
????????return?"WorkThread?number:"?+?worker_num
????????????????+?"??wait?task?number:"?+?taskQueue.size();
????}
????/**
?????*?內(nèi)部類,工作線程
?????*/
????private?class?WorkThread?extends?Thread?{
????????@Override
????????public?void?run()?{
????????????Runnable?r?=?null;
????????????try?{
????????????????while?(!isInterrupted())?{
????????????????????r?=?taskQueue.take();
????????????????????if?(r?!=?null)?{
????????????????????????System.out.println(getId()?+?"?ready?exec?:"?+?r);
????????????????????????r.run();
????????????????????}
????????????????????r?=?null;//help?gc;
????????????????}
????????????}?catch?(Exception?e)?{
????????????????//?TODO:?handle?exception
????????????}
????????}
????????public?void?stopWorker()?{
????????????interrupt();
????????}
????}
}
JDK中的線程池和工作機(jī)制
線程池的創(chuàng)建
ThreadPoolExecutor,jdk所有線程池實(shí)現(xiàn)的父類
各個參數(shù)的意義
int corePoolSize:線程池核心線程數(shù),池內(nèi)線程數(shù)
int maximumPoolSize:線程池允許的最大線程數(shù),如果阻塞隊(duì)列也滿了, ?maximumPoolSize?
long keepAliveTime:線程空閑下來的存活時間,這個數(shù)值,只有在線程池內(nèi)的線程數(shù)量 > corePoolSize的時候才會有作用,它決定著 >?corePoolSize數(shù)量的線程的空閑下來的存活時間
TimeUnit unit:存活時間單位
BlockingQueue
ThreadFactory threadFactory:創(chuàng)建線程的工廠,給新創(chuàng)建的線程賦予名字
RejectedExecutionHandler handler:?線程池的飽和策略,當(dāng)線程池的最大允許數(shù)被沾滿了,阻塞隊(duì)列也沾滿了,那么再放入任務(wù)如何處理
飽和策略:
AbortPolicy:直接拋出異常
CallerRunsPolicy:用調(diào)用者所在的線程執(zhí)行任務(wù)
DiscardOldestPolicy:丟棄阻塞隊(duì)列中最老的任務(wù),也就是最靠前的任務(wù)
DiscardPolicy:當(dāng)前任務(wù)直接丟棄
如果這個策略都不是你想要的,可以自己實(shí)現(xiàn) RejectedExecutionHandler?接口,來完成自己的策略,比如寫數(shù)據(jù)庫,寫日志,或者緩存到其他的里面,等以后再撿回來
注意:剛初始化的線程池當(dāng)中是沒有線程的,只有當(dāng)往里面投遞任務(wù)才會創(chuàng)建,如果想一行來就讓里面的線程數(shù)等于corePoolSize可以調(diào)用prestartAllCoreThreads方法
提交任務(wù)
void execute(Runnable command):用于提交無返回的任務(wù)
Future
關(guān)閉線程池
shutdown(),shutdownNow()
shutdownNow(),設(shè)置線程池的狀態(tài),還會嘗試停止正在運(yùn)行或者暫停任務(wù)的線程
shutdown()設(shè)置線程池狀態(tài),只會中斷所有沒有執(zhí)行任務(wù)的線程
工作機(jī)制

?
?

在加入任務(wù)的會后會先判斷一下當(dāng)前線程池中的核心線程數(shù)時候小于corePoolSize,如果小于,創(chuàng)建新的線程如果大于嘗試放入阻塞隊(duì)列中,如果場入失敗,那么將嘗試創(chuàng)建新的線程用于運(yùn)行任務(wù),如果創(chuàng)建新的線程也失敗的話,那么將執(zhí)行飽和策略
合理配置線程池
根據(jù)任務(wù)的性質(zhì)來:計(jì)算密集型(CPU),IO密集型,混合型
計(jì)算密集型:
加密,大數(shù)分解,正則.....,線程數(shù)適當(dāng)小一些,
最大推薦:機(jī)器的Cpu核心數(shù)+1,為什么要+1,防止頁缺失
JAVA獲取CPU核心數(shù):
Runtime.getRuntime().availableProcessors();IO密集型:
讀取文件,數(shù)據(jù)庫連接,網(wǎng)絡(luò)通訊,線程數(shù)適當(dāng)大一些
最大推薦:機(jī)器的CPU核心數(shù)*2
混合型:
盡量拆分,IO密集型遠(yuǎn)遠(yuǎn)大于計(jì)算密集型,拆分意義不大,IO密級型~計(jì)算密級型,拆分才有意義
注意:隊(duì)列的選擇上應(yīng)該使用有界隊(duì)列,無界隊(duì)列可能會導(dǎo)致內(nèi)存溢出,OOM
預(yù)定義的線程池:
FixedThreadPool:
創(chuàng)建固定線程數(shù)量的,適用于負(fù)載較重的服務(wù)器,使用了LinkedBlockingQueue作為阻塞隊(duì)列

?
SingleThreadExecutor
創(chuàng)建單個線程,需要保證順序執(zhí)行任務(wù),不會有多個線程活動,使用了LinkedBlockingQueue作為阻塞隊(duì)列
?
? 
?
? CachedThreadPool
會根據(jù)需要來創(chuàng)建新的線程,適用于執(zhí)行很多很短期異步任務(wù)的程序,使用了SynchronousQueue作為阻塞隊(duì)列

?
? ScheduledThreadPool
需要定期執(zhí)行周期任務(wù),Timer不建議使用了。
newSingleThreadScheduledExecutor:只包含一個線程,只需要單個線程執(zhí)行周期任務(wù),保證順序的執(zhí)行各個任務(wù)
newScheduledThreadPool 可以包含多個線程的,線程執(zhí)行周期任務(wù),適度控制后臺線程數(shù)量的時候
方法說明:
schedule:只執(zhí)行一次,任務(wù)還可以延時執(zhí)行
scheduleAtFixedRate:提交固定時間間隔的任務(wù)
scheduleWithFixedDelay:提交固定延時間隔執(zhí)行的任務(wù)
兩者的區(qū)別:

scheduleAtFixedRate任務(wù)超時:
規(guī)定60s執(zhí)行一次,有任務(wù)執(zhí)行了80S,下個任務(wù)馬上開始執(zhí)行
第一個任務(wù) 時長 80s,第二個任務(wù)20s,第三個任務(wù) 50s
第一個任務(wù)第0秒開始,第80S結(jié)束;
第二個任務(wù)第80s開始,在第100秒結(jié)束;
第三個任務(wù)第120s秒開始,170秒結(jié)束
第四個任務(wù)從180s開始
參加代碼:ScheduleWorkerTime類,執(zhí)行效果如圖:
建議在提交給ScheduledThreadPoolExecutor的任務(wù)要住catch異常。
? Executor框架圖

Executor框架基本使用流程

粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
??????

??長按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?
