相關(guān)源碼:boy-learning-thread
個(gè)人博客:http://bruce.bugmakers.club
內(nèi)容來自《網(wǎng)易微專業(yè) - 高性能編程章節(jié)》
線程池原理
1、為什么要用線程池
線程是不是越多越好?
1右遭、線程在 java 中是一個(gè)對(duì)象郊供,更是操作系統(tǒng)中的資源驴娃,線程創(chuàng)建妓忍、銷毀需要時(shí)間。如果 “創(chuàng)建時(shí)間 + 銷毀時(shí)間 > 執(zhí)行任務(wù)時(shí)間” 就恨不合算峭沦。
2、java 對(duì)象占用堆內(nèi)存征炼,操作系統(tǒng)線程占用系統(tǒng)內(nèi)存析既,根據(jù) jvm 規(guī)范,一個(gè)線程默認(rèn)最大棧大小 1M谆奥,這個(gè)空間是需要從系統(tǒng)內(nèi)存中分配的眼坏。線程過多,會(huì)消耗很多內(nèi)存酸些。
3宰译、操作系統(tǒng)需要頻繁切換線程上下文(大家都想被執(zhí)行),線程過多會(huì)影響性能魄懂。
線程池的推出沿侈,就是為了方便的控制線程數(shù)量。
2市栗、線程池原理 - 概念
1缀拭、線程池管理器:用于創(chuàng)建并管理線程池,包括:創(chuàng)建線程池填帽、銷毀線程池蛛淋、添加新任務(wù);
2篡腌、工作線程:線程池中的線程铣鹏,在沒有任務(wù)時(shí)處于等待狀態(tài),可以循環(huán)的執(zhí)行任務(wù)哀蘑;
3、任務(wù)接口:每個(gè)任務(wù)必須實(shí)現(xiàn)的接口葵第,以供工作線程調(diào)度任務(wù)的執(zhí)行绘迁,它主要規(guī)定了:任務(wù)的入口、任務(wù)執(zhí)行完后的收尾工作卒密、任務(wù)的狀態(tài)缀台;
4、任務(wù)隊(duì)列:用于存放沒有處理的任務(wù)哮奇。以供一種緩沖機(jī)制膛腐。
2.1、線程池 API - 接口定義和實(shí)現(xiàn)類
類型 | 名稱 | 描述 |
---|---|---|
接口 | Executor | 最上層接口鼎俘,定義了執(zhí)行任務(wù)的方法 execute |
接口 | ExecutorService | 繼承了 Executor 接口哲身,拓展了 Callable、Future贸伐、關(guān)閉方法 |
接口 | ScheduledExecutorService | 繼承了 ExecutorService勘天,增加了定時(shí)任務(wù)相關(guān)的方法 |
實(shí)現(xiàn)類 | ThreadPoolExecutor | 基礎(chǔ)、標(biāo)準(zhǔn)的線程池實(shí)現(xiàn)類 |
實(shí)現(xiàn)類 | ScheduledThreadPoolExecutor | 繼承了 ThreadPoolExecutor,實(shí)現(xiàn)了 ScheduledExecutorService 中相關(guān)定時(shí)任務(wù)的方法 |
可以認(rèn)為 ScheduledThreadPoolExecutor 是最豐富的實(shí)現(xiàn)類脯丝。
2.2商膊、線程池 API - 方法定義
ExecutorService
// 監(jiān)測(cè) ExecutorService 是否已關(guān)閉,直到所有任務(wù)完成執(zhí)行宠进,或超時(shí)發(fā)生晕拆,或當(dāng)前線程被中斷
awaitTermination(long timeout, TimeUnit unit);
// 執(zhí)行給定的任務(wù)集合,執(zhí)行完畢后材蹬,返回結(jié)果
invokeAll(Collection<? extends Callable<T>> tasks);
// 執(zhí)行給定的任務(wù)集合实幕,執(zhí)行完畢或者超時(shí)后,返回結(jié)果赚导,其他任務(wù)終止
invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit);
// 執(zhí)行給定的任務(wù)集合茬缩,任意一個(gè)任務(wù)執(zhí)行成功后,返回結(jié)果吼旧,其他任務(wù)終止
invokeAny(Collection<? extends Callable<T>> tasks);
// 執(zhí)行給定的任務(wù)集合凰锡,任意一個(gè)任務(wù)執(zhí)行成功或超時(shí)后,返回結(jié)果圈暗,其他任務(wù)終止
invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit);
// 如果線程池關(guān)閉掂为,則返回treu
isShutdown();
// 如果關(guān)閉后,所有任務(wù)都已經(jīng)執(zhí)行完畢员串,則返回true
isTerminated();
// 優(yōu)雅關(guān)閉線程池勇哗,之前已經(jīng)提交的任務(wù)將被執(zhí)行,但不接受新的任務(wù)
shutdown();
// 嘗試停止所有正在執(zhí)行的任務(wù)寸齐,停止等待任務(wù)的處理欲诺,并返回等待執(zhí)行任務(wù)的列表
shutdownNow();
// 提交一個(gè)用于執(zhí)行的 Callable 返回任務(wù),并返回一個(gè) Future 對(duì)象渺鹦,用于獲取 Callable 執(zhí)行結(jié)果
submit(Callable<T> task);
// 提交可運(yùn)行任務(wù)以執(zhí)行扰法,并返回一個(gè) Future 對(duì)象,執(zhí)行結(jié)果為 null
submit(Runnable task);
// 提交可運(yùn)行任務(wù)以執(zhí)行塞颁,并返回一個(gè) Future 對(duì)象祠锣,執(zhí)行結(jié)果為傳入的 result
sumit(Runnable task, T result);
ScheduledExecutorService
// 創(chuàng)建并執(zhí)行一個(gè)一次性任務(wù)妆棒,過了延遲時(shí)間就會(huì)被執(zhí)行
schedule(Callable<T> callable, long delay, TimeUnit unit);
schedule(Runnable command, long delay, TimeUnit unit);
// 創(chuàng)建并執(zhí)行一個(gè)周期性任務(wù)馋评,過了延遲時(shí)間會(huì)第一次執(zhí)行,執(zhí)行過程發(fā)生異常蜕青,那么任務(wù)就停止了
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit);
scheduleAtFixedRate 方法渺绒,一次任務(wù)執(zhí)行時(shí)長(zhǎng)超過周期時(shí)間,下次任務(wù)在該次任務(wù)執(zhí)行完之后,立即執(zhí)行。
schedualWithFixedDelay 方法,一次任務(wù)執(zhí)行時(shí)間長(zhǎng)超過周期時(shí)間固蛾,下次任務(wù)會(huì)在該次任務(wù)執(zhí)行完之后趾诗,計(jì)算執(zhí)行延時(shí)(再等延遲一個(gè) period 的時(shí)長(zhǎng))郑兴。
2.3犀斋、線程池 API - Executors 工具類
你也可以自己實(shí)例化線程池,也可以用 Executors 創(chuàng)建線程池的工廠類情连,常用方法如下:
1) newFixedThreadPool(int nThreads)
創(chuàng)建一個(gè)固定大小叽粹、任務(wù)隊(duì)列無界的線程池。核心線程數(shù) = 最大線程數(shù)却舀。
2) newCachedThreadPool()
創(chuàng)建一個(gè)大小無界的緩存線程池虫几。他的任務(wù)隊(duì)列是一個(gè)同步隊(duì)列。
任務(wù)加入到池中挽拔,如果池中有空閑線程辆脸,則用空閑線程執(zhí)行,如無則創(chuàng)建新的線程執(zhí)行螃诅,池中的線程空閑超過60秒啡氢,將被銷毀釋放。
線程數(shù)隨任務(wù)的多少變化州刽。適用于任務(wù)量不可控空执,且執(zhí)行耗時(shí)較小的異步任務(wù)。
核心線程數(shù) = 0穗椅, 最大線程數(shù) = Integer.MAX_VALUE
3) newSingleThreadExecutor()
只有一個(gè)線程來執(zhí)行無界任務(wù)隊(duì)列的單一線程池辨绊。該線程池確保任務(wù)按照加入隊(duì)列的順序一個(gè)一個(gè)一次執(zhí)行。
當(dāng)唯一的線程因任務(wù)異常終止時(shí)匹表,將創(chuàng)建一個(gè)新的線程來執(zhí)行后續(xù)的任務(wù)门坷。
與 newFixedThreadPool(1) 的區(qū)別在于,單一線程池的大小在 new SingleThreadExecutor 方法中硬編碼袍镀,不能再改變默蚌。
4) newScheduledThreadPool(int corePoolSize)
能定時(shí)執(zhí)行任務(wù)的線程池。該池的核心線程數(shù)由參數(shù)來指定苇羡,最大線程數(shù) = Integer.MAX_VALUE
3绸吸、線程池原理 - 任務(wù) execute 過程
1、是否達(dá)到核心線程數(shù)量设江?沒達(dá)到锦茁,創(chuàng)建一個(gè)工作線程來執(zhí)行任務(wù)。
2叉存、任務(wù)隊(duì)列是否已滿码俩?沒滿,則將新提交的任務(wù)存儲(chǔ)在任務(wù)隊(duì)列里邊歼捏。
3稿存、是否達(dá)到線程池最大數(shù)量笨篷?沒達(dá)到,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)瓣履。
4率翅、最后,執(zhí)行拒絕策略來處理這個(gè)任務(wù)拂苹。
4安聘、線程數(shù)量
如何確定合適數(shù)量的線程?
計(jì)算型任務(wù):cpu數(shù)量的1~2倍瓢棒。
IO型任務(wù):根據(jù)具體的IO阻塞時(shí)長(zhǎng)進(jìn)行考量決定浴韭。
如 Tomcat 中默認(rèn)的最大線程數(shù)為:200
也可以考慮根據(jù)需要在一個(gè)最小數(shù)量和最大數(shù)量間自動(dòng)增減線程數(shù)。
監(jiān)控CPU的使用率脯宿,如果遠(yuǎn)小于80%則使用不合理念颈,大于也不可理,接近則表示使用良好连霉。