線程池解析第一章-源碼解析
線程池解析第二章-線程池源碼問題總結
上篇文章分析了關于線程池在源碼層面對提交任務的處理方式粹污,但是光光去看源碼韧涨,有些值得思考的地方可能會被忽略西疤,但是這些點可能是對線程池理解至關重要对室,理解了才能對線程池有更為整體的認識割去,本篇文章主要是對閱讀源碼的一個總結,把一些問題拿出來進行一個總結
Q1:為什么要使用線程池
A:當創(chuàng)建一個線程和銷毀一個線程所需要的時間大于這個線程的執(zhí)行時間遇汞,那么這就是一個資源上的浪費未妹,當線程創(chuàng)建過多了以后,線程間的切換則會消耗大量的內存和資源空入,線程池中保存了空閑的線程络它,可以利用空閑的線程,避免創(chuàng)建銷毀的資源浪費
Q1-0:線程和操作系統(tǒng)及CPU的關系
A:線程實際上是操作系統(tǒng)的概念歪赢,java本身并不能創(chuàng)建線程化戳,而是調用操作系統(tǒng)提供的接口,實現(xiàn)線程的創(chuàng)建埋凯,控制点楼,銷毀,實際創(chuàng)建的是操作系統(tǒng)線程白对,在經(jīng)由操作系統(tǒng)分配到空閑的CPU上
- 上面兩個問題是對于線程最基本的概念掠廓,只有理解了線程的由來,才能對多線程有更完整的認識
Q2:線程池中的核心線程是如何循環(huán)利用的
A:其實這個問題在閱讀完源碼后就很好理解了甩恼,當執(zhí)行runWorker()方法的時候蟀瞧,首先會去獲取任務沉颂,然后有一個while語句去判斷當前任務是否為空,或者調用getTask()方法去判斷結果是否為空而對應的getTask方法則是去循環(huán)獲取workqueue阻塞隊列中的任務悦污,此處實現(xiàn)了核心線程的循環(huán)利用铸屉,只不過此處需要注意對有,核心線程在默認情況下會一直存在切端,當核心線程在被allowCoreThreadTimeOut為true后彻坛,也是可以被銷毀的,但是非核心線程在keepalivetime過期后如果沒有獲取到任務將會自動被銷毀
Q3:在線程池中,如果線程沒有全部執(zhí)行任務踏枣,為什么不是利用閑置的而是創(chuàng)建新的線程呢昌屉?
A:這是我之前代碼沒讀完的時候產(chǎn)生的一個問題,看完代碼后這個問題就很簡單了椰于,對于核心線程來說怠益,如果隊列任務為空仪搔,那么這些核心線程就是屬于空閑狀態(tài)瘾婿,所以核心線程是否處于空閑狀態(tài)一個很關鍵的所在就是工作隊列中是否有任務,如果想要利用空閑線程烤咧,那么任務隊列就必須有任務偏陪,但如果此刻線程池線程數(shù)還沒有達到核心線程數(shù),這時候則會創(chuàng)建核心線程煮嫌,但是當線程數(shù)量大于核心線笛谦,則會加入到工作隊列當中,這時候就成功利用閑置的核心線程昌阿,但是不會增加新的線程
Q4:Set類型的workers又是做什么的饥脑,為什么worker會加入到workers當中
A:workers是當前線程池中所有線程的集合,當有新的線程被創(chuàng)建后懦冰,都會被加入到這個集合當中灶轰,對于workers的操作,只有在拿到mainLock的時候才能進行訪問刷钢,在shutdown()等關閉方法中笋颤,被用作檢測以確保呼叫者可以中斷workers集合中的每一個工作線程,并對所有workers中的工作線程進行中斷
Q5:在addWorker方法中内地,當任務加入任務隊列中且線程池中可用線程數(shù)量為0時伴澄,調用addWorker(null, false)是什么意思,當無法再向任務隊列中添加任務時阱缓,調用addWorker(command, false)的作用是什么非凌,他和addWorker(command, true)方法有什么區(qū)別
A: 調用addWorker(null, false)的作用,addWorker是為了創(chuàng)建線程處理傳遞的任務荆针,此處傳遞的task為空敞嗡,說明此處創(chuàng)建了一個任務為空的worker對象并蝗,目的只有一個,就是處理之前被加入到任務隊列中的task秸妥。
addWorker(command, false)和addWorker(command, true)有什么區(qū)別滚停,在addWorker方法中,只是簡單的做了一個線程數(shù)量的判斷粥惧,但是在getTask方法中键畴,如果是非核心線程,在keepAliveTime內沒有獲取到任務突雪,將會銷毀非核心線程
- Q2~5主要說的是線程池實現(xiàn)邏輯上的問題颊咬,如有不對的地方歡迎指正
Q6:在線程池當中撕彤,submit()/execute()之間關系及區(qū)別
A:線程池的執(zhí)行入口通常為submit()/execute(),而submit()方法可以返回線程執(zhí)行的結果,使用execute()方法則不需要返回執(zhí)行的結果,submit()方法實際是調用了ThreadPoolExecutor父類AbstractExecutorService的submit方法援岩,該方法可以接收兩個類型的參數(shù),一個是Runnable對象帆调,一個是callable對象嚣镜,但是在submit()方法中會調用newTaskFor()返回一個...
FutureTask對象,在構造FutureTask對象的時候辰狡,如果傳入的是Runnable類型锋叨,也會被轉換成Callable類型
Q7:在線程池當中,runnable和callable的關系及區(qū)別
A:線程池在接受任務的時候宛篇,往往接受兩種類型娃磺,一種是runnable一種是callable類型,runnable類型需要實現(xiàn)run方法叫倍,而callable類型則需要實現(xiàn)call方法偷卧,在線程池進行提交任務的操作中,通過submit提交是可以事先獲取一個futureTask對象的返回值吆倦,說明提交到submit中的對象是實現(xiàn)了call方法的听诸,但在源碼中submit是可以接受runable對象,這是因為submit在接受到了runnable對象后逼庞,會將runnable轉換成callable對象蛇更,我前面說了,callable對象是需要實現(xiàn)call方法的赛糟,但是被轉換的runnbale只有run方法派任,這一步實際上源碼的轉換類幫助我們重寫了call方法,在call方法中嵌套了run方法
Q8:線程池執(zhí)行任務類璧南,如果是通過submit方法提交的掌逛,那么如何將計算的結果放入futureTask對象當中的
A:在進行submit方法進行提交任務的時候,實際返回的是一個 RunnableFuture對象司倚,在線程池調用run方法的時候豆混,實際調用的是RunnableFuture接口篓像,對應的實現(xiàn)類是futureTask,在futureTask對應接口RunnableFuture的run方法的實現(xiàn)方法中皿伺,則是調用了任務類的call方法员辩,并將獲取到的結果存入futureTask對象當中,對于futureTask中的實現(xiàn)邏輯不是很了解的同志可以參考我之前的文章
Future三重奏第二章:FutureTask源碼解析
- Q6,Q7,Q8這三個模塊的問題需要放在一起理解鸵鸥,他不是單獨的孤零零的問題奠滑,而是綜合在一起的,彼此緊密關聯(lián)的問題點妒穴,主要說明的是對于線程池中不同提交方式帶來的兩者之間的差別和實現(xiàn)結果上的不同宋税,且其中涉及到futureTask類的理解,如果對futureTask不是很清楚讼油,那么對于線程池的實現(xiàn)杰赛,在理解上就不能說是特別的通透,建議有興趣的朋友可以看看我之前寫的文章
Q9:內部類Worker是繼承實現(xiàn)了AQS的矮台,目的是什么乏屯,體現(xiàn)在什么地方
A:主要從Worker對創(chuàng)建以及使用上來闡述這個問題,分為以下幾點
1:當線程池創(chuàng)建Worker對象的時候嘿架,對state對默認賦值為-1瓶珊,此處對目的是為了防止線程在執(zhí)行前被標記中斷
2:在線程池調用關閉操作的時候啸箫,首先會獲取線程集耸彪,依次取出當中每一個線程,當線程還沒有執(zhí)行任務and xxxx 的時候忘苛,將會被標記打斷蝉娜,此處注意,如果線程對state為-1扎唾,無法被標記為中斷
3:在線程池中執(zhí)行runWorker方法的時候召川,首先需要調用w.unlock()方法,調用此方法將當前線程的state置為0胸遇,目的是為了防止線程在被調用前就被標記中斷荧呐,調用此方法后,線程就可以被置為中斷狀態(tài)
Q10:interrupt,interrupted,isinterrupted的關系和作用
A:在這里提出這個問題是因為在上面的問題中纸镊,當線程加鎖后倍阐,需要判斷當前線程是否需要被中斷,在一系列當判斷后逗威,將會對線程進行中斷操作峰搪,所以此處單獨提出來看
1:interrupt的作用是對當前線程進行中斷,只能作用在當前線程
2:interrupted的作用是返回當前線程的中斷狀態(tài)凯旭,并清除中斷位
3:isInterrupted的作用就是返回當前線程的中斷狀態(tài)概耻,不做任何操作
Q11:對于線程池當中執(zhí)行拒絕策略的解析
A:當線程池中任務隊列數(shù)量已滿且線程數(shù)量超過線程池最大數(shù)量的時候使套,將會執(zhí)行拒絕策略,拒絕策略有四類鞠柄,具體執(zhí)行何種拒絕策略需要視創(chuàng)建線程池時選擇的拒絕策略而定矫膨,四種拒絕策略會采用四種不同的處理方式期奔,且都為ThreadPoolExecutor內部類侧馅,實現(xiàn)了接口RejectedExecutionHandler中的執(zhí)行方法馁痴,建議此處查看相關源碼
1:AbortPolicy:丟棄任務并拋出RejectedExecutionException異常罗晕。
2:DiscardPolicy:也是丟棄任務小渊,但是不拋出異常。
3:DiscardOldestPolicy:丟棄隊列最前面的任務呐萨,然后重新嘗試執(zhí)行任務(重復此過程)谬擦,通過e.getQueue().poll()實現(xiàn)拒絕策略
4:CallerRunsPolicy:由調用線程處理該任務惨远,相關代碼:r.run();直接調用線程處理