Java面試題沖刺第二十五天–並發編程3

面試題1:你瞭解線程池麼?簡單介紹一下。

java提供的一個java.util.concurrent.Executor接口的實現用於創建線程池。

線程池是一種多線程處理形式,處理過程中將任務提交到線程池,任務的執行交由線程池來管理。如果每個請求都創建一個線程去處理,那麼服務器的資源很快就會被耗盡,使用線程池可以減少創建和銷毀線程的次數,每個工作線程都可以被重復利用,可執行多個任務。

假設一個服務器完成一項任務,創建線程時間T1 ,在線程中執行任務的時間T2,銷毀線程時間為T3。如果:T1 + T3 遠大於 T2,則可以采用線程池,大大縮短T1、T3時間,以提高服務器性能。

一個線程池包括以下四個基本組成部分: 

1.線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷毀線程池,添加新任務;

2.工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;

3.任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行,它主要規定瞭任務的入口,任務執行完後的收尾工作,任務的執行狀態等;

4.任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩沖機制。

在這裡插入圖片描述

當線程池中有任務需要執行時,線程池會判斷如果池裡的線程數沒有占滿就會新建線程池進行任務執行,如果線程池中的線程數量已經超過核心線程數,這時候任務就會被放入任務隊列中排隊等待執行;如果任務隊列也滿瞭,並且線程池沒有達到最大線程數,就會新建非核心線程來執行任務;如果超過瞭最大線程數,就會執行飽和策略(拒絕執行)。

追問1:連接池 和 線程池是一個意思麼?有什麼區別?

不同點

連接池:

  • 連接池是面向數據庫連接的
  • 連接池是為瞭優化數據庫連接資源
  • 連接池有點類似在客戶端做優化

數據庫連接是一項有限的昂貴資源,一個數據庫連接對象均對應一個物理數據庫連接,每次操作都打開一個物理連接,使用完都關閉連接,這樣造成系統的性能低下。

數據庫連接池的解決方案是在應用程序啟動時建立足夠的數據庫連接,並將這些連接組成一個連接池,由應用程序動態地對池中的連接進行申請、使用和釋放。對於多於連接池中連接數的並發請求,應該在請求隊列中排隊等待。並且應用程序可以根據池中連接的使用率,動態增加或減少池中的連接數。

線程池:

  • 線程池是面向後臺程序的
  • 線程池是是為瞭提高內存和CPU效率
  • 線程池有點類似於在服務端做優化

線程池是一次性創建一定數量的線程(應該可以配置初始線程數量的),當用請求過來不用去創建新的線程,直接使用已創建的線程,使用後又放回到線程池中。

避免瞭頻繁創建線程,及銷毀線程的系統開銷,提高是內存和CPU效率。

相同點

都是事先準備好資源,避免頻繁創建和銷毀的代價。

面試題2:線程池中核心線程數量大小你是怎麼設置的?

分析一般從幾個角度考慮:

  • 任務的性質:CPU密集型的任務、IO密集型任務、混合型任務。
  • 任務的優先級:高、中、低
  • 任務執行時間:長、中、短
  • 任務的依賴性:是否依賴其它系統資源,如數據庫的連接等。

在這裡插入圖片描述

cpu密集型

盡量減少線程數;比如像加解密,壓縮、計算等一系列需要大量耗費 CPU 資源的任務,大部分場景下都是純 CPU 計算。盡量使用較小的線程池,一般為CPU核心數+1。因為CPU密集型任務使得CPU使用率很高,若開過多的線程數,會造成CPU過度切換。

IO密集型

任務盡量加大線程數,因為io不占用cpu的資源。比如像 MySQL 數據庫、文件的讀寫、網絡通信等任務,這類任務不會特別消耗 CPU 資源,但是 IO 操作比較耗時,會占用比較多時間。可以使用稍大的線程池,一般為CPU核心數 * 2。IO密集型任務CPU使用率並不高,因此可以讓CPU在等待IO的時候有其他線程去處理別的任務,充分利用CPU時間。

混合型

盡量根據實際情況進行拆分,根據運行時間來決定。

可見,線程的平均工作時間所占比例越高,就需要越少的線程;線程的平均等待時間所占比例越高,就需要越多的線程;

追問1:核心線程數量過大或過小會造成什麼後果?

當線程池中核心線程數量過大時,線程與線程之間會爭取CPU資源,這樣就會導致上下文切換。過多的上下文切換會增加線程的執行時間,影響瞭整體執行的效率;

多線程編程中一般線程的個數都大於CPU核心的個數,而一個CPU核心在任意時刻隻能被一個線程使用,為瞭讓這些線程都能得到有效的執行,CPU采取的策略是為瞭每個線程分配時間片並輪轉的形式。當一個線程的時間片用完的時候就會重新處於就緒狀態讓其他線程使用,這個過程就屬於一次上下文切換。

當線程池中的核心線程數量過少時,如果統一時間有大量任務需要處理,可能會導致大量任務在任務隊列中排隊等待執行,甚至會出現隊列滿瞭之後任務無法執行的情況,或者大量任務堆積在任務隊列導致內存溢出(OOM)。

面試題3:線程池都有哪些狀態呀?

線程池的5種狀態:RunningShutDownStopTidyingTerminated

在這裡插入圖片描述

  • RUNNING:線程池的初始化狀態是RUNNING,能夠接收新任務,以及對已添加的任務進行處理。
  • SHUTDOWN:線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。 調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN。
  • STOP:線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,並且會中斷正在處理的任務。 調用線程池的shutdownNow()接口時,線程池由(RUNNING or SHUTDOWN ) -> STOP。
  • TIDYING:當所有的任務已終止,ctl記錄的”任務數量”為0,線程池會變為TIDYING狀態。當線程池變為TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變為TIDYING時,進行相應的處理;可以通過重載terminated()函數來實現。
  • TERMINATED:線程池徹底終止,就變成TERMINATED狀態。線程池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。

  當線程池在SHUTDOWN狀態下,阻塞隊列為空並且線程池中執行的任務也為空時,就會由 SHUTDOWN -> TIDYING。 當線程池在STOP狀態下,線程池中執行的任務為空時,就會由STOP -> TIDYING。

追問1:什麼條件下會進入TERMINATED狀態

  •  線程池不是RUNNING狀態;
  • 線程池狀態不是TIDYING狀態或TERMINATED狀態;
  • 如果線程池狀態是SHUTDOWN並且workerQueue為空;
  • workerCount為0;
  • 設置TIDYING狀態成功。

總結

本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: