摘要:線程池為線程生命周期的開銷和資源不足問題提供了解決方案。狀態(tài)說明線程池處于狀態(tài),不接收新任務,不處理已提交的任務,并且會中斷正在處理的任務。線程池中允許的最大線程數(shù)。線程池的飽和策略。
線程池為線程生命周期的開銷和資源不足問題提供了解決方 案。通過對多個任務重用線程,線程創(chuàng)建的開銷被分攤到了多個任務上。線程實現(xiàn)方式
Thread、Runnable、Callable
//實現(xiàn)Runnable接口的類將被Thread執(zhí)行,表示一個基本任務 public interface Runnable { //run方法就是它所有內(nèi)容,就是實際執(zhí)行的任務 public abstract void run(); }
//Callable同樣是任務,與Runnable接口的區(qū)別在于它接口泛型,同時它執(zhí)行任務候帶有返回值; //Callable的使用通過外層封裝成Future來使用 public interface Callable{ //相對于run方法,call方法帶有返回值 V call() throws Exception; }
注意:啟動Thread線程只能用start(JNI方法)來啟動,start方法通知虛擬機,虛擬機通過調(diào)用器映射到底層操作系統(tǒng),通過操作系統(tǒng)來創(chuàng)建線程來執(zhí)行當前任務的run方法
Executor框架Executor接口是線程池框架中最基礎(chǔ)的部分,定義了一個用于執(zhí)行Runnable的execute方法。
從圖中可以看出Exectuor下有一個重要的子接口ExecutorService,其中定義了線程池的具體行為:
execute(Runnable runnable):執(zhí)行Runnable類型的任務
submit(task):用來提交Callable或者Runnable任務,并返回代表此任務的Future對象
shutdown():在完成已經(jīng)提交的任務后封閉辦事,不在接管新的任務
shutdownNow():停止所有正在履行的任務并封閉辦事
isTerminated():是一個鉤子函數(shù),測試是否所有任務都履行完畢了
isShutdown():是一個鉤子函數(shù),測試是否該ExecutorService是否被關(guān)閉
ExecutorService中的重點屬性:private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1;
ctl:對線程池的運行狀態(tài)和線程池中有效線程的數(shù)量進行控制的一個字段,它包含兩部分信息:線程池的運行狀態(tài)(runState)和線程池內(nèi)有效線程的數(shù)量(workerCount)。
這里可以看到,使用Integer類型來保存,高3位保存runState,低29位保存workerCount。COUNT_BITS 就是29,CAPACITY 就是1左移29位減1(29個1),這個常量表示workerCount的上限值,大約是5億。
ctl相關(guān)方法:
//獲取運行狀態(tài) private static int runStateOf(int c) { return c & ~CAPACITY; } //獲取活動線程數(shù) private static int workerCountOf(int c) { return c & CAPACITY; } //獲取運行狀態(tài)和活動線程數(shù)的值 private static int ctlOf(int rs, int wc) { return rs | wc; }
線程池的狀態(tài):
RUNNING = -1 << COUNT_BITS? //高3位為111 SHUTDOWN = 0 << COUNT_BITS? //高3位為000 STOP = 1 << COUNT_BITS? //高3位為001 TIDYING = 2 << COUNT_BITS? //高3位為010 TERMINATED = 3 << COUNT_BITS? //高3位為011
1、RUNNING
狀態(tài)說明:線程池處于RUNNING狀態(tài),能夠接收新任務,以及對已添加的任務進行處理。
狀態(tài)切換:線程池的初始化狀態(tài)是RUNNING。換句話說,線程池一旦被創(chuàng)建,就處于RUNNING狀態(tài),并且線程池中的任務數(shù)為0。
2、SHUTDOWN
狀態(tài)說明:線程池處于SHUTDOWN狀態(tài),不接收新任務,能夠處理已經(jīng)添加的任務。
狀態(tài)切換:調(diào)用shutdown()方法時,線程池由RUNNING -> SHUTDOWN。
3、STOP
狀態(tài)說明:線程池處于STOP狀態(tài),不接收新任務,不處理已提交的任務,并且會中斷正在處理的任務。
狀態(tài)切換:調(diào)用線程池中的shutdownNow()方法時,線程池由(RUNNING or SHUTDOWN) -> STOP。
4、TIDYING
狀態(tài)說明:當所有的任務已經(jīng)停止,ctl記錄“任務數(shù)量”為0,線程池會變?yōu)門IDYING狀態(tài)。當線程池處于TIDYING狀態(tài)時,會執(zhí)行鉤子函數(shù) terminated()。 terminated()在ThreadPoolExecutor類中是空, 的,若用戶想在線程池變?yōu)門IDYING時,進行相應處理,可以通過重載 terminated()函數(shù)來實現(xiàn)。
狀態(tài)切換:當線程池在SHUTDOWN狀態(tài)下,阻塞隊列為空并且線程池中執(zhí)行任務也為空時,就會由SHUTDOWN -> TIDYING。當線程池在STOP狀態(tài)下,線程池中執(zhí)行的任務為空時,就會由STOP-> TIDYING。
5、TERMINATED
狀態(tài)說明:線程池線程池徹底停止,線程池處于TERMINATED狀態(tài),
狀態(tài)切換:線程池處于TIDYING狀態(tài)時,執(zhí)行完terminated()之后, 就會由TIDYING->TERMINATED。
線程池使用RunTask類:
public class RunTask implements Runnable { public void run() { System.out.println("Thread name:"+Thread.currentThread().getName()); } }
ExecutorSample類:
public class ExecutorSample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5); for (int i=0;i<20;i++){ //提交任務無返回值 executor.execute(new RunTask()); //任務執(zhí)行完成后有返回值 Future
線程池的具體使用:
ThreadPoolExecutor 默認線程池
ScheduledThreadPoolExecutor 定時線程池
ThreadPoolExecutor線程池的創(chuàng)建:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
corePoolSize:線程池中的核心線程數(shù)。當提交一個任務時,線程池創(chuàng)建一個新線程執(zhí)行任務,直到當前線程數(shù)等于corePoolSize;如果當前線程數(shù)為corePoolSize,繼續(xù)提交的任務被保存到阻塞隊列中,等待被執(zhí)行;如果執(zhí)行了線程池的prestartAllCoreThreads()方法,線程池會提前創(chuàng)建并啟動所有核心線程。
maximumPoolSize:線程池中允許的最大線程數(shù)。如果當前阻塞隊列滿了,且繼續(xù)提交任務,則創(chuàng)建新的線程執(zhí)行任務,前提是當前線程數(shù)小于maximumPoolSize。
keepAliveTime:線程池維護線程所允許的空閑時間。當線程池中的線程數(shù)量大于corePoolSize時候,如果這時候沒有新的任務提交,核心線程外的線程不會立即被銷毀,而是會等待,直到等待的時間超過了keepAliveTime
unit:keepAliveTime的單位時間
workQueue:用于保存等待被執(zhí)行的任務的阻塞隊列,且任務必須實現(xiàn)Runnable接口,在JDK中提供了如下阻塞隊列:
ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊列,按FIFO排序任務。
LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的阻塞隊列,按FIFO排序任務,吞吐量通常要高于ArrayBlockingQueue。
SynchronousQueue:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常高于LinkedBlockingQueue。
PriorityBlockingQueue:具有優(yōu)先級的無界阻塞隊列。
threadFactory:ThreadFactory 類型的變量,用來創(chuàng)建新線程。默認使用ThreadFactory.defaultThreadFactory來創(chuàng)建線程, 會使新創(chuàng)建線程具有相同的NORM_PRIORITY優(yōu)先級并且都是非守護線程,同時也設(shè)置了線程名稱。
handler:線程池的飽和策略。當阻塞隊列滿了,且沒有空閑的工作隊列,如果繼續(xù)提交任務,必須采用一種策略處理該任務.
線程池的監(jiān)控:
public long getTaskCount() //線程池已執(zhí)行與未執(zhí)行的任務總數(shù) public long getCompletedTaskCount() //已完成的任務數(shù) public int getPoolSize() //線程池當前的線程數(shù) public int getActiveCount() //線程池中正在執(zhí)行任務的線程數(shù)量線程池的原理
如果當前運行的線程少于corePoolSize,則創(chuàng)建新線程來執(zhí)行任務(注意這一個步驟需要獲取全局鎖)。
如果運行的線程等于或多于corePoolSize,則將任務加入BlockingQueue。
如果無法將任務加入BlockingQueue(隊列已滿),則創(chuàng)建新的線程來處理任務(注意這一個步驟需要獲取全局鎖)。
如果創(chuàng)建的新線程將使當前運行的線程超出maximumPoolSize,任務將被執(zhí)行飽和策略。
ThreadPoolExecutor 采用上述的設(shè)計思路,是為執(zhí)行execute()方法時,盡可能避免獲取全局鎖(一個嚴重的可伸縮瓶頸)。在ThreadPoolExecutor完成預熱之后,幾乎所有的execute()方法調(diào)用都是在執(zhí)行步驟2,而步驟2不需要獲取全局鎖。
還沒關(guān)注我的公眾號?
掃文末二維碼關(guān)注公眾號【小強的進階之路】可領(lǐng)取如下:
學習資料: 1T視頻教程:涵蓋Javaweb前后端教學視頻、機器學習/人工智能教學視頻、Linux系統(tǒng)教程視頻、雅思考試視頻教程;
100多本書:包含C/C++、Java、Python三門編程語言的經(jīng)典必看圖書、LeetCode題解大全;
軟件工具:幾乎包括你在編程道路上的可能會用到的大部分軟件;
項目源碼:20個JavaWeb項目源碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/76253.html
摘要:一該類繼承了類,該類里面封裝了一個重連機制,而注冊中心核心的功能注冊訂閱取消注冊取消訂閱,查詢注冊列表都是調(diào)用了我上一篇文章源碼解析三注冊中心開篇中講到的實現(xiàn)方法,畢竟這種實現(xiàn)注冊中心的方式是默認的方式,不過推薦使用,這個后續(xù)講解。 注冊中心——dubbo 目標:解釋以為dubbo實現(xiàn)的注冊中心原理,解讀duubo-registry-default源碼 dubbo內(nèi)置的注冊中心實現(xiàn)方式...
摘要:當然,如果你的核心數(shù)夠多,到個線程的并行度不滿足的話,也可以自定義一個線程池來執(zhí)行,不過這樣的話,要注意自己維護這個線程池的初始化,釋放等等操作了。 事情起源于一個bug排查,一個AsyncTask的子類,執(zhí)行的時候發(fā)現(xiàn)onPreExecute方法執(zhí)行了,doInBackground卻遲遲沒有被調(diào)用。懂AsyncTask一些表面原理的都知道,onPreExecute方法是在主線程執(zhí)行,...
閱讀 2384·2021-11-23 09:51
閱讀 5854·2021-09-22 15:39
閱讀 3409·2021-09-02 15:15
閱讀 3559·2019-08-30 15:54
閱讀 2418·2019-08-30 15:53
閱讀 1459·2019-08-30 14:04
閱讀 2515·2019-08-29 18:33
閱讀 2480·2019-08-29 13:08