線程池的幾個面試重要考點
阿粉有點驚嘆最近的面試題,因為從之前的基礎的面試題,到之后的一些涉及到分布式和微服務的面試題,再到現在的線程池的一些面試題,反正不同的面試官,就有不同的針對方向,可能現在的面試官比較想考驗你的多方面的能力吧,而最近,一個讀者就反饋給了阿粉說,面試官全程就從線程這塊入手,整的自己有點尷尬,但是好在有驚無險的入職了,我們來看看面試官都問了什么內容?
進程和線程的概念,你能說一下自己的理解么?這個問題,有點基礎,不過肯定是之后的開胃小菜。
進程和線程的關系
進程就是應用程序在內存中分配的空間,也就是正在運行的程序,各個進程之間互不干擾。同時進程保存著程序每一個時刻運行的狀態。
讓一個線程執行一個子任務,這樣一個進程就包含了多個線程,每個線程負責一個單獨的子任務。
進程是一個獨立的運行環境,而線程是在進程中執行的一個任務。他們兩個本質的區別是是否單獨占有內存地址空間及其它系統資源(比如I/O)
總得來說就是,線程是屬于進程中的一個任務,應該算是包含的關系。
進程是操作系統進行資源分配的基本單位,而線程是操作系統進行調度的基本單位。
多進程的方式也可以實現并發,為什么我們要使用多線程?這個問題就有意思了,你如果不是很了解的話,這個問題還真不好回答。
多進程方式確實可以實現并發,但使用多線程,是比多進程有好處的。
1.進程間的通信比較復雜,而線程間的通信比較簡單,通常情況下,我們需要使用共享資源,這些資源在線程間的通信比較容易。
2.進程是重量級的,而線程是輕量級的,故多線程方式的系統開銷更小。
資源浪費屬于一方面的有點,通信簡單也是另外一方面的優點,就憑借這兩點的內容,還能選擇多進程?
線程池的內容
你在工作中使用過線程池么?為什么使用線程池?這個問題有點尷尬,為什么這么說?
如果你說你沒用過,那你這在面試官這里就相當于只寫 CRUD 的邏輯業務了,也不整點其他的內容。
如果你說你用過,你就得回答接下來的一系列關于線程池的問題了。這個阿粉還是推薦,實話實話,就算你沒用過,那么也別瞎扯,不然你這給自己挖的坑,肯定自己得跳下去。
那么我們就從為什么使用線程池來入手分析唄。
首先我們就要思考一件事,不使用線程池的話,創建線程有什么弊端么?
在java中,如果每個請求到達就創建一個新線程,那對服務器的資源消耗是不是有點大,創建線程,銷毀線程,創建線程,銷毀線程,然后再各種線程之間來回的切換,這一來一回,是不是感覺資源浪費就體現出來了。
那么線程池會避免這個情況么?
這就出來了優點1了
創建/銷毀線程需要消耗系統資源,線程池可以復用已創建的線程。
雖然這個優點很明確,但是還不是主要原因,主要原因如下:
控制并發的數量。并發數量過多,可能會導致資源消耗過多,從而造成服務器崩潰。(主要原因)
可以對線程做統一管理
分析一下線程池的原理 Java中的線程池頂層接口是Executor接口,但是使用的肯定不是這個,是 ThreadPoolExecutor
我們看看 ThreadPoolExecutor 構造函數
竟然參數這么多,分別都代表什么意思呢?
int corePoolSize:該線程池中核心線程數最大值。
int maximumPoolSize:該線程池中線程總數最大值。
long keepAliveTime:非核心線程閑置超時時長。
TimeUnit unit:keepAliveTime的單位。
BlockingQueue workQueue:阻塞隊列,維護著等待執行的Runnable任務對象。
corePoolSize核心線程最大值:這個值怎么確定?
一般這個問題是相對來說比較棘手的,如果面試官問這個問題,那一般的同學肯定頭大,我知道啥意思,但是這個怎么設置,我怎么定義呢?
其實有個計算公式:
線程等待時間所占比例越高,需要越多線程。線程CPU時間所占比例越高,需要越少線程
maximumPoolSize :線程池中線程總數最大值
這個值實際上就是 核心線程數 + 非核心線程數量
keepAliveTime: 這個值如果設定了,那么非核心線程如果處于閑置狀態超過該值,就會被銷毀。
BlockingQueue:阻塞隊列
看樣子感覺像 MQ 里面的東西,想到隊列,我們就又能聯想到生產者和消費者,這時候就出現了個問題,為什么要有阻塞隊列呢?
是不是就出現了消費者模式,生產者一直生產資源,消費者一直消費資源,資源存儲在一個緩沖池中。
我們在實現這個模式的時候,多個線程操作共享變量,于是就帶來了線程安全性的問題,造成重復消費和死鎖,這時候阻塞隊列就出現了,當緩沖池空了,我們需要阻塞消費者,喚醒生產者;當緩沖池滿了,我們需要阻塞生產者,喚醒消費者。
而BlockingQueue提供了線程安全的隊列訪問方式,并發包下很多高級同步類的實現都是基于BlockingQueue實現的。
也就是說,你就只負責生產和消費,安全問題,JDK 來給你保證。
說到這里,我們不在繼續往下延伸了,等下次阿粉直接在吧 BlockingQueue 完全的分析一波,應為 BlockingQueue 絕對得需要一個長篇的內容才能解釋清楚。
分析完里面的參數,這時候,就得來看看線程池是怎么處理線程任務的,不然那怎么和面試官battle。
線程池是如何處理內部的線程任務的
在 execute 方法中,ctl.get()是獲取線程池狀態。
流程如下:
1,首先線程池判斷基本線程池是否已滿,沒滿,創建一個工作線程來執行任務。滿了,則進入下個流程。
2,其次線程池判斷工作隊列是否已滿?沒滿,則將新提交的任務存儲在工作隊列里。滿了,則進入下個流程。
3,最后線程池判斷整個線程池是否已滿,沒滿,則創建一個新的工作線程來執行任務,滿了,則交給飽和策略來處理這個任務。
如果你能把這些在面試的時候說清楚,那么至少在線程池這個知識點上,你是沒有任何問題了,這樣就可以愉快并且開心的走下一個知識點了。