1. 程式人生 > 實用技巧 >Offer快到碗裡來——聊聊執行緒池

Offer快到碗裡來——聊聊執行緒池

微信公眾號:大黃奔跑
關注我,可瞭解更多有趣的面試相關問題。

寫在之前

Hello,大家好,我是隻會寫HelloWorld的程式設計師大黃。

廢話不多說,今天直接進入正題,聊聊Java中執行緒池的原理及面試問題。整體的文章會從面試題目展開,講解面試點的過程中穿插原始碼的剖析。

我個人比較推崇學習方式是,先知道某個技術是幹嘛的,再瞭解為啥有這個技術,有什麼好處。只有知道了為什麼、才能更好的知道是什麼。

按照國際慣例先來看看一般面試中執行緒池會如何考察?

面試問題概覽

下面是我在網上搜刮的一些關於執行緒池的面試題目,都是許多同學的親身經歷的。

可以先看看這些面試題目,如果你面對這些題目,該如何回答呢?

  1. 執行緒池引數的具體含義【eBay一面】
  2. 建立執行緒的方式 執行緒池的主要引數,執行緒池在提交任務之後處理的過程【攜程】
  3. 執行緒池瞭解嗎?當程式用close()方法後,執行緒池裡面的連線會關閉嗎?【京東數科】
  4. 你瞭解哪些執行緒池型別【小鵬汽車】
  5. 執行緒池作用有哪些作用【招商銀行】
  6. 說說執行緒池操作,引數【位元組跳動】
  7. 說一下執行緒池的實現,引數有哪些,不同的使用場景分別用什麼執行緒池【阿里巴巴】
  8. 執行緒池引數?執行緒池為什麼用new的不好?【58面試】

可以看到執行緒池相關的知識點被問到的概率還是挺大的,可以將問題做個分類,大概可以分為:
第一類:執行緒池的使用(包括如何建立執行緒池、執行緒池各個引數含義、拒絕策略、什麼場景會使用)
第二類:執行緒池的原理了解(比如執行緒池底層實現、任務提交之後執行緒池如何工作的、執行緒池的執行過程)

面試回顧

面試一般面試上來肯定做一番自我介紹了,然後直奔主題。

面試官:大黃同學,我看你簡歷中寫了利用執行緒池解決專案中效能問題,那我們先簡單聊聊執行緒池吧。說說執行緒池有哪些作用?

哈哈哈,問吧,執行緒池我可是早就準備過的,送分題啊。

大黃:面試官您好,執行緒池主要yo三個好處:

1、降低資源的消耗。通過重複利用已經建立的執行緒降低執行緒建立和執行緒銷燬的資源損耗。(用過的執行緒可以重複利用)

2、提高響應速度。當任務達到時,不需要等待執行緒建立就可以執行。(任務來了就可以用)

3、提供執行緒的可管理性。執行緒池本身提供了很多方法供使用者監控、設定執行緒池。(建立一堆執行緒,有人幫你管理)

面試官:那建立執行緒有哪些引數可以控制呢,他們分別有什麼含義呢

大黃:主要引數有

1、corePoolSize:主要是執行緒池的核心執行緒數。當工作執行緒數小於該值時,新來的任務會建立新執行緒來處理,並且執行緒會一直存活,不會過期。
2、maximumPoolSize:執行緒池的最大執行緒數。當工作中的執行緒數已經超過了最大執行緒數時,會預設啟用拒絕策略
3、keepAliveTime:執行緒空閒之後過期的時間,只有執行緒數大於corePoolSize時或者開啟allowCoreThreadTimeOut引數時,該值才起作用
4、unitkeepAliveTime的時間單位
5、workQueue:阻塞佇列,執行緒數超過corePoolSize時,請求的執行緒會先進入阻塞佇列中,只有當阻塞佇列也滿了,才會去建立執行緒
6、threadFactory:建立執行緒的工廠,所有的執行緒都是通過該工廠建立
7、allowCoreThreadTimeOut:是否允許核心執行緒過期
預設是false,此時,核心執行緒空閒也不會過期;如果是true,核心執行緒會等keepAliveTime時間,然後自動過期
8、handler:執行緒滿時或者停止時的拒絕策略

讀者可以看看執行緒池最全的構造方法

publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue,
ThreadFactorythreadFactory,
RejectedExecutionHandlerhandler)
{
....
}

面試官:可以詳細說說執行緒池有哪些拒絕策略嗎?

大黃:執行緒池主要由以下四種拒絕策略

  1. 呼叫者執行緒來處理被拒絕的任務。比如我執行緒A提交任務給執行緒池,如果執行緒池此時處理不了,被拒絕了,則會交由執行緒A來處理(俗話說,哪兒來的回哪兒去

  2. 直接拒絕,並且往外丟擲異常,該策略為JDK預設的拒絕策略。 執行緒池已經來不及處理了,偷懶,直接丟擲異常。

  3. 直接廢棄被拒絕的任務。執行緒A提交一個任務給執行緒池,執行緒池處理不了,直接不管了(俗稱,裝睡,假裝不知道

  4. 廢棄最早被提交未被處理的請求。比如執行緒A提交10任務給執行緒池,如果再提交新的任務給執行緒池,執行緒池會先丟擲最先提交的任務(俗稱,渣男,喜新厭舊

咳咳,括號內的內容面試中就別回答了。。。

面試官:平時有看過執行緒池的底層實現嗎?能簡單說說執行緒池底層是如何做的嗎?

其實這種問法跟上面"執行緒池在提交任務之後處理的過程?"本質問的問題是一樣的

大黃:執行緒池接受一個新提交的任務時執行緒池處理思路大概如下:

  1. 判斷執行緒池工作中執行緒是否已經超過核心執行緒數(corePoolSize)。如果沒有超過,則建立一個工作執行緒來執行任務;如果正在工作的執行緒數超過了核心執行緒數,則進入下一個流程。

  2. 判斷執行緒池的阻塞佇列(等待佇列)是否已經被佔滿。如果沒有佔滿,則將任務新增到阻塞佇列中;如果已經佔滿,則進入下一階段。

  3. 判斷執行緒池工作中執行緒是否已經超過最大執行緒數。如果沒有超過,則建立一個新的工作執行緒來執行任務;如果超過了最大執行緒數,則需要根據設定的拒絕策略來處理新的任務。

大黃小提醒:可以用一個簡單的例子描述,假設一個銀行網點一般值班的有4個櫃員,總共可以後備的櫃員有10個,休息裡面有可以做20人的凳子。剛開始業務不忙的時候,只有2個客戶來辦理業務,只要不超過4個客戶,就可以用常用的值班櫃員中找一個來服務;如果超過了4個,則可以讓客戶在休息區等待;如果等待任務超過了20個客戶,還有新客戶到來,則需要拉來未加班的同事了。

整體的工作原理如下:

執行緒池工作原理(圖片思想借鑑於《Java併發程式設計藝術》)

大家可以記住這個例子,面試中不一定能夠完整的回答上上面的所有內容,但是隻要具體意思沒有跑偏,面試官會領會到的。

面試官:嗯,可以,瞭解的聽清楚的嘛。那你平時工作中有地方用到執行緒池了嗎?

這點就是面試官比較聰明的地方,說了那麼多,給我講講你工作的例子吧。正所謂:talk is cheap, show me your code.這裡給大家說一個我工作中是使用的場景吧。

大黃:我做的一個專案中為了快速的響應使用者的請求。比如在使用者獲取具體的商品推薦策略中,需要查詢商品資料和營銷資料,然後將對應的商品資訊和營銷資訊進行組裝,返回給使用者。
剛開始用序列去請求,響應速度比較慢,使用者遲遲查不到資料,如果一個頁面半天都刷不出,使用者可能就放棄檢視這個商品了。為了解決這個問題,採用執行緒池,併發的請求商品和營銷資訊,縮短總體響應時間。

並行請求

並行請求

序列請求

序列請求

使用執行緒池也是有考量的,這種場景最重要的就是獲取最大的響應速度去滿足使用者,所以應該不設定佇列去緩衝併發任務,調高corePoolSize和maxPoolSize去儘可能創造多的執行緒快速執行任務。

面試官:那你專案中設定的執行緒池各個引數分別是多少呢?

大黃:具體的執行緒池大小設定如下:

@Bean
ThreadPoolTaskExecutorProductExecutor(){

RejectedExecutionHandlerrejectedExeHandler=(r,executor)->{
log.error("Executor_reject_handler");
thrownewException("System.error");
};
ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();
executor.setCorePoolSize(200);
executor.setMaxPoolSize(1000);
executor.setKeepAliveSeconds(60);
executor.setQueueCapacity(2000);
executor.setRejectedExecutionHandler(rejectedExeHandler);
returnexecutor;
}

面試官:那一般專案執行緒池大小設定有什麼規則嗎?

大黃:
這種需要區分任務的型別,可以分為 CPU 密集型和 I/O 密集型,根據不同的任務型別,我們計算執行緒數的方法也不一樣。
1、CPU 密集型任務。
這種任務消耗的主要是 CPU 資源,可以將執行緒數設定為 N(CPU 核心數)+1,比 CPU 核心數多出來的一個執行緒是為了防止執行緒偶發的缺頁中斷,或者其它原因導致的任務暫停而帶來的影響。一旦任務暫停,CPU 就會處於空閒狀態,而在這種情況下多出來的一個執行緒就可以充分利用 CPU 的空閒時間。
2、I/O 密集型任務
這種任務應用起來,系統會用大部分的時間來處理 I/O 互動,而執行緒在處理 I/O 的時間段內不會佔用 CPU 來處理,這時就可以將 CPU 交出給其它執行緒使用。因此在 I/O 密集型任務的應用中,我們可以多配置一些執行緒,具體的計算方法是 2N。

上面的結論來自《Java併發程式設計藝術》,這種知識理論的設定,可以參考,但是實際中建議參考美團的技術部落格《Java執行緒池實現原理及其在美團業務中的實踐》

面試官:好的,那我們聊聊別的吧。

只要每個問題都可以回答到如此,何愁Offer。

預告時間:下篇會仔細分析執行緒池的原始碼,可能會有點晦澀,我儘可能寫的淺顯一些。

總結

最後大黃分享多年面試心得。面試中,面對一個問題,大概按照總分的邏輯回答即可。先直接丟擲結論,然後舉例論證自己的結論。一定要第一時間抓住面試官的心裡,否則容易給人抓不著重點或者不著邊際的印象。

本意是想著更可能回答的十全十美,無奈知識淺薄,難免會有紕漏,如果你發現了錯誤的地方,可以加我的微信交流。(微信公眾號沒有留言。)

加個人微信,一起交流

番外

另外,關注大黃奔跑公眾號,第一時間收穫獨家整理的面試實戰記錄及面試知識點總結。

我是大黃,一個只會寫HelloWorld的程式設計師,咱們下期見。

關注大黃,充當offer收割機