圖解 | 阿里巴巴面試官愛問的線程池到底是什麼?

2021-02-11 CoderW
前言

前幾天小強去阿里巴巴面試Java崗,止步於二面。

他和我訴苦自己被虐的多慘多慘,特別是深挖線程和線程池的時候,居然被問到不知道如何作答。

對於他的遭遇,結合他過了一面的那個嘚瑟樣,我深表同情(加大力度)!

好了,不開玩笑了,在和小強的面試題中,我選取了幾個比較典型的線程和線程池的問題。

Java中的線程和作業系統的線程有什麼關係?

調用start方法是如何執行run方法的?

線程池提交任務有哪幾種方式?分別有什麼區別?

談談你對阻塞隊列的理解。

常見的線程池有哪些?為什麼阿里不允許使用 Executors 去創建線程池?

線程池任務調度的流程大致講一下。

線程池裡面的線程執行異常了會怎麼樣?

核心線程和非核心線程是如何區分的?

想要答對這些問題,並不是很難,但是想要答好,我覺得是非常考驗個人功底的。

為了弄清這些問題,我連夜加急,採訪了「線程」,下面是線程的自述。


我是誰

我是一個線程,一個底層的打工人。

總有人把我和進程搞混,但其實我和進程的區別很大。

進程是程序的一次執行,CPU的資源都是分發給進程而不是分發給我們線程,進程是資源分配的最小單位,一個進程可以包含很多向我這樣的線程。

我們線程是CPU調度執行的最小單位,真正的打工人。


Java中的線程

在Java裡面,我的名字叫做java.lang.Thread。

需要注意的是,調用run方法和執行一個普通方法沒有區別。想要真正的創建一個線程並啟動,需要調用我的start方法。

有一點我必須告訴你,就是我也是有小弟的。

在JVM裡面,我有一個JavaThread的小弟,他幫我聯繫作業系統的osthread線程。

調用我的start方法之後,具體的執行流程是這樣的:

當然了,這個過程省略了很多細節,不過很明確的是,我和內核線程是一一對應的。

調度我就相當於調度內核線程,而調度內核線程需要在用戶態和內核態之間切換,這個過程開銷是非常大的。

所以,創建我成本是很高的,一定要慎重。

線程池

和你們人類一樣,我也有著精彩的一生,也會經歷出生(創建)、奮鬥(Running)、死亡(銷毀)等過程,今天我主要和你講述的是我打工奮鬥的生活。

原來我是打零工的,有人需要我的時候就創建一個我,等我完成工作就把我銷毀。

上面也提到過,我和內核線程是一對一的,創建和銷毀的過程是非常消耗資源的,所以這樣的成本非常高。

於是,有人就想了一個辦法,開了一個公司,也就是你們說的線程池。

線程池公司統一管理調度我們線程。我們在線程池裡面重複著等待工作——完成工作的步驟。

這樣我就可以日復一日年復一年的重複打工了,這種提供了減少對象數量從而改善應用所需的對象結構的方式的模式,被你們人類叫做「享元模式」。

線程池公司有很多種,但都離不開這幾個主要指標:

maximumPoolSize:正式工+臨時工最大數量。keepAliveTime:臨時工多久沒做事情會被開除。threadFactory:行政部,負責招聘培訓員工的。handler:業務部接收業務到達上限了的處理方式。阻塞隊列

線程池中的workQueue是一個阻塞隊列,用於存放線程池未能及時處理執行的任務。

它的存在既解耦了任務的提交與執行,又能起到一個緩衝的作用。

阻塞隊列有很多,下面我帶你了解一下常見的阻塞隊列。

ArrayBlockingQueue

基於數組實現的有界阻塞隊列,創建的時候需要指定容量。此類型的隊列按照FIFO(先進先出)的規則對元素進行排序。

LinkedBlockingQueue

基於鍊表實現阻塞隊列,默認大小為Integer.MAX_VALUE。按照FIFO(先進先出)的規則對元素進行排序。

SynchronousQueue

一個不存儲元素的阻塞隊列。每一個put操作必須阻塞等待其他線程的take操作,take操作也必須等待其他線程的put操作。

PriorityBlockingQueue

一個基於數組利用堆結構實現優先級效果的無界隊列,默認自然序排序,也可以自己實現compareTo方法自定義排序規則。

DelayedWorkQueue

一個實現了優先級隊列功能且實現了延遲獲取的無界隊列,在創建元素時,可以指定多久多久才能在隊列中獲取當前元素。只有延時期滿了後才能從隊列中獲取元素。

拒絕策略

當任務隊列滿了之後,如果還有任務提交過來,會觸發拒絕策略,常見的拒絕策略有:

AbortPolicy:丟棄任務並拋出異常,默認該方式。

CallerRunsPolicy:由調用線程自己處理該任務。誰調用,誰處理。

DiscardPolicy:丟棄任務,但是不拋出異常。

DiscardOldestPolicy:拋棄任務隊列中最舊的任務,也就是最先加入隊列的,再把這個新任務添加進去。先從任務隊列中彈出最先加入的任務,空出一個位置,然後再次執行execute方法把任務加入隊列。

當然,除了以上這幾種拒絕策略,你也可以根據實際的業務場景和業務需求去自定義拒絕策略,只需要實現RejectedExecutionHander接口,自定義裡面的rejectedExecution方法。

運行流程

我們每個線程會被包裝成Worker,線程池裡面有一個HashSet存放Worker。

當有任務提交過來之後:

首先檢測線程池運行狀態,如果不是RUNNING,則直接拒絕,線程池要保證在RUNNING的狀態下執行任務。如果線程池中Worker的數量小於核心線程數,就會去創建一個新的線程,也就是招聘一個正式工讓他執行任務。如果Worker的數量大於或者等於核心線程數,就會把任務放到阻塞任務隊列裡面。如果任務隊列滿了還有任務過來,如果臨時工名額沒有滿(workerCount < maximumPoolSize),就去招聘臨時工讓臨時工執行任務。如果臨時工名額都滿了,觸發任務拒絕策略。

總結而言,就是核心線程能幹的事情儘量不去創建非核心線程,這是線程池很關鍵的一點。

new ThreadPoolExecutor(4,  8, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(4));

以這個線程池為例,下面是他的任務提交和執行流程:

有哪些線程池

我有過四段工作經歷,每段經歷都有著精彩的故事。

SingleThreadExecutor

SingleThreadExecutor是我加入的第一家線程池,這是一家創業公司,整個線程池就只有我一個線程。

所有的任務都由我幹,而且任務隊列是一個無界隊列。就是說,打工的線程只有我一個,但是需求任務可以是無限多。

在需求任務很多的時候,經常出現任務處理不過來的情況,導致任務堆積,出現OOM。

但因為所有的活都是我幹,沒有繁瑣的溝通成本,不需要處理線程同步的問題,這算是這種線程池的一個優點吧。

這種線程池適用於並發量不大且需要任務順序執行的場景。

FixedThreadPool

後來公司倒閉了,我又加入了一個叫FixedThreadPool的線程池。

FixedThreadPool和SingleThreadExecutor唯一不同的地方就是核心線程的數量,FixedThreadPool可以招收很多的打工線程。

在這裡,我不再是孤軍奮鬥了,我有了一群共同打拼的小夥伴,大家一起完成任務,一起承擔壓力。

可這種線程池還是存在一個問題——任務隊列是無界的,需求任務過多的話,還是會造成OOM。

這種線程池線程數固定,且不被回收,線程與線程池的生命周期同步的線程池,適用於任務量比較固定但耗時長的任務。

CachedThreadPool

後來,為了離家更近,我離職了。加入了一家叫CachedThreadPool的線程池,進去之後,卻發現這是一家外包公司。

這種線程池裡面沒有一個核心線程(正式工),一有需求就去招聘一個非核心線程(臨時工)。

如果一個線程任務幹完了之後,60秒之後沒有新的任務就會被辭退。

這種線程池的任務隊列採用的是SynchronousQueue,這個隊列是無法插入任務的,一有任務就創建一個線程執行,如果並發高且任務耗時長,創建太多線程也是可能導致OOM的。所以CachedThreadPool比較適合任務量大但耗時少的任務。

ScheduleThreadPool

經歷了外面的風風雨雨,我覺得還是找份固定的工作比較可靠,於是我加入了一家叫做ScheduleThreadPool的國企。

在這裡,工作比較的輕鬆,多數情況下,我只需要在固定的時間幹固定的活。

任務忙不過來的時候,公司也會招聘一些臨時工幫忙處理,臨時工幹完活就會被辭退。

綜合來說,這類線程池適用於執行定時任務和具體固定周期的重複任務。由於採用的任務隊列是DelayedWorkQueue無界隊列,所以也是有OOM的風險的。


總結

好了,關於線程的故事就告一段落了。關於線程池的應用實踐,我們下次再聊。

文章開頭的面試題在大部分在文中都能找到答案,對於沒有提到的,這裡做一個補充:

1. 線程池提交任務有哪幾種方式?分別有什麼區別?

有execute和submit兩種方式

execute只能提交Runnable類型的任務,無返回值。submit既可以提交Runnable類型的任務,也可以提交Callable類型的任務,會有一個類型為Future的返回值,但當任務類型為Runnable時,返回值為null。

execute在執行任務時,如果遇到異常會直接拋出,而submit不會直接拋出,只有在使用Future的get方法獲取返回值時,才會拋出異常。

2. 線程池裡面的線程執行異常了會怎麼樣?

如果一個線程執行任務的過程中出現異常,那麼這個線程對應的Worker會被移出線程池,該線程也會被銷毀回收。

同時會通過指定的線程工廠創建一個線程,並封裝成Worker放入線程池代替移除的Worker。

3. 核心線程能被回收嗎?

核心線程默認不會被回收。但是可以調用allowCoreThreadTimeOut讓核心線程可以被回收。

需要注意的是,調用這個方法的線程池必須將keepAliveTime設置為大於0,否則會拋出異常。

4. 核心線程和非核心線程是如何區分的?

核心線程和非核心線程是一個抽象概念,只是用於更好的表述線程池的運行邏輯,實際上都對應作業系統的osThread,都是重量級線程。

在新增Worker的時候,通過一個boolean表達是核心線程還是非核心線程,本質上兩者沒有什麼不同。

5. 為什麼阿里不允許使用 Executors 去創建線程池?

FixedThreadPool 和 SingleThreadPool:允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。

CachedThreadPool:允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。

總結來說就是,使用Executors創建線程池會容易忽視線程池的一些屬性,使用不當容易造成資源耗盡。


寫在最後

這個世界上或許沒有線程,又或許人人都是線程。

好了,今天的文章就到這裡了。

最後,感謝你的閱讀!

我是CoderW,一個普通的程式設計師。

點個關注,我們下期再見!

相關焦點

  • java新手揭秘:阿里巴巴為何禁止使用Executors來創建線程池
    當一個java新手從不斷地Curd階段跳出來之後,就會學習java的並發,並行等高階用法,自然就會用到線程、線程池,線程池的好處這裡就不做詳細解釋,你應該會學習到Executors創建線程池的四個方法, 分別是:newFixedThreadPool
  • 阿里面試官鬼得很,問我為什麼他們阿里要禁用Executors創建線程池?
    作者:何甜甜在嗎來源:http://rrd.me/eUh6V看阿里巴巴開發手冊並發編程這塊有一條:線程池不允許使用
  • 面試官:太平洋的中間是什麼?碩士答:海水,當場被淘汰
    我一個朋友:小巫,碩士畢業,英語專八,在網上投了簡歷差不多100份的時候;有收到一家公司的面試邀請;在初試的時候,因為學歷背景很輕鬆的就進入了複試(據她本人描述:面試著中她的學歷是最高的);再接下來準備複試的過程中,面試官問了個的問題:「太平洋的中間是什麼?」
  • 四面阿里斬獲offer定級P7,2020最新最全阿里巴巴68道高級面試題
    面試:如果不準備充分的面試,完全是浪費時間,更是對自己的不負責。今天給大家分享下我整理的Java架構面試專題及答案(文末見面試答案),其中大部分都是大企業面試常問的面試題,可以對照這查漏補缺,當然了,這裡所列的肯定不可能覆蓋全部方式,不過也希望能對即將找工作的朋友起到一些幫助!
  • Java中線程池,你真的會用嗎?
    在文中有這樣一段描述:可以通過Executors靜態工廠構建線程池,但一般不建議這樣使用。關於這個問題,在那篇文章中並沒有深入的展開。作者之所以這麼說,是因為這種創建線程池的方式有很大的隱患,稍有不慎就有可能導致線上故障。本文我們就來圍繞這個問題來分析一下為什麼JDK自身提供的構建線程池的方式並不建議使用?
  • 1000個並發線程,10臺機器,每臺機器4核,設計線程池大小
    美團給出了一個讓面試官虎軀一震的回答》《吐血輸出:2萬字長文帶你細細盤點五種負載均衡策略》下面我會針對我感受到的這兩個考點去進行分析。線程池設計我們先想簡單一點:1000 個並發線程交給 10 臺機器去處理,那麼 1 臺機器就是承擔 100 個並發請求。
  • 職場新人必看:面試時面試官最愛問哪些問題?
    這是有道理的:儘管其中一些怪異的面試問題用來表明潛在員工的坦誠意願,但更傳統的問題則更全面地描繪了候選人對職位的適合程度。 LinkedIn引用了以下傳統面試問題:說說你自己。你最大的優點是什麼?你最大的缺點是什麼?我們為什麼應該錄用你?你為什麼想在這裡工作?告訴我你擔任領導職務的時間。告訴我你在團隊中取得成功的時間。你的同事會怎麼說你?
  • 到底如何設置Java線程池的大小?
    那麼在用到並發功能的過程中,就肯定會碰到下面這個問題並發線程池到底設置多大呢?通常有點年紀的程式設計師或許都聽說這樣一個說法 (其中 N 代表 CPU 的個數)     CPU 密集型應用,線程池大小設置為 N + 1     IO 密集型應用,線程池大小設置為 2N 這個說法到底是不是正確的呢?其實這是極不正確的。那為什麼呢?
  • 線程池的核心參數及工作原理
    JDK中線程池的類是ThreadPoolExecutor,如果不藉助Executors.newFixedThreadPool(3)等方式創建,可以用構建函數來創建線程池,它的代碼是這樣的,public ThreadPoolExecutor(int
  • Java之線程池的簡單介紹
    通過使用線程池,可以使線程復用,就是執行完一個任務,不銷毀,繼續執行其他任務。其實線程池就相當於一個容器(集合),裡面有很多線程。線程池原理圖解其實線程池就是一個容納多線程的容器,其中線程可以反覆使用,省去了創建線程對象的操作,無需反覆創建線程而消耗過多資源。
  • 從使用到原理,探究Java線程池
    什麼是線程池當我們需要處理某個任務的時候,可以新創建一個線程,讓線程去執行任務。線程池的字面意思就是存放線程的池子,當我們需要處理某個任務的時候,可以從線程池裡取出一條線程去執行。為什麼需要線程池首先我們要知道不用線程池,直接創建線程有什麼弊端:第一個是創建與銷毀線程的開銷,Java中的線程是映射到作業系統線程上的,頻繁地創建和銷毀線程會極大地損耗系統的性能。
  • 面試技巧:HR最愛問的幾個問題,回答時千萬不要踩到雷區!
    迷妹提議讓我寫寫比較擅長面試環節!別光寫工作中的問題。因為一個好的面試,對於面試者和企業都是很重要的一個環節!企業和崗位不同,面試方式會有很大差別,沒有固定的形式、問題和答案。今天給大家分享分享,身為HR最愛問的幾個問題,來判斷面試者是否適合這份工作,從而決定是否錄取,是否該給到適合的薪資?所以回答時千萬不要踩到雷區!
  • 面試官:用100個字介紹你!真正打動面試官的到底是什麼?
    前言:奇葩的面試和程式化的面試在電視劇《誰為愛情買單》中有過這麼一段比較奇葩的面試:面試官:請用100個字介紹一下你自己!王志飛:只用100個字?為什麼?面試官:因為我只有這麼長的時間聽!拿我曾經的面試經歷舉例,只有兩個問題:面試官:桌子上有一張紙,上面有兩個問題,你準備一下,準備好了可以開始回答!在準備的過程中,如果你有什麼想法可以記下來!第一個問題是如果你參加工作以後,被分配到鄉鎮,你應該怎麼說服自己服從安排?
  • 「技術」其實銀行就是一個Java線程池
    銀行是個線程池周末,銀行七個窗口只來了兩個值班的員工,那麼這兩個員工我們就叫做核心員工數那天由於不知道什麼原因,來銀行辦業務的人特別多,兩個員工很快就忙不過來了,那麼新來的人怎麼辦呢?工作人員可以選擇讓新的員工明兒,或者一會兒再來,這個讓新的人什麼時候來的做法,就是一種拒絕策略的選擇。除了上面的情況還會有另一種情況,5個人來加班之後,效率提升,很快就把所有的業務做完了,然後銀行就沒有人辦業務了,那麼這多出來的五個人要一直在銀行待著嗎?
  • 面試官問我:線程池中多餘的線程是如何回收的?
    不過,我倒是對線程池是如何回收工作線程比較感興趣,所以簡單分析了一下,加深對線程池的理解吧。下面以JDK1.8為例進行分析1. runWorker(Worker w)工作線程啟動後,就進入runWorker(Worker w)方法。
  • 史丹福大學校友面試官告訴你:名校到底要什麼
    很多人關心一個問題:外國學校有什麼申請攻略。他們眼中的申請攻略包括面試技巧、材料撰寫、自選材料的選擇等。我也是一個孩子的母親,關心孩子的教育,這些年擔任校友面試官的經歷給我一個強烈的感受,中國家庭少的不是攻略與技巧,而是一些有關心態的東西,而這正是校友面試官在尋找的東西。
  • 問你這些問題,面試官內心OS到底是什麼?
    可以說是五花八門面試官們提出這些問題時,內心的OS是什麼? 面試官內心OS:你只是為了跳槽加薪才來的嗎?你真實的動機是什麼? 是能對公司和職位的好處頭頭是道,眼神中透露興奮,還是在面試前毫無準備,對公司和職位一知半解,純粹是因為被HR或獵頭找到才來?
  • 內向的人最害怕的10個面試問題,偏偏面試官最愛問!
    後來我就把面試官可能問到的問題,一個個列下來,把回答寫成逐字稿,找來幾個HR朋友,讓他們從自己每天面試十幾個人的角度和經驗,一字一句地幫我修改稿子,然後背下來,直至脫稿。這幫我從一個offer都拿不到,到連拿四個offer,並且每次面試都能和面試官侃侃而談2個小時以上。
  • HR面試官說「你先回去等通知」到底是什麼意思?
    面試結束後如果你沒有被當場錄用,HR面試官最後說的一定是「你先回去等通知」這句話,那這句話到底是什麼意思呢?說實話,這句話大部分情況確實只是表面意思,沒有特別的含義,但如果結合面試的實際狀態和環境,也可以作為判斷面試效果的依據。
  • Java線程池其實看懂了也很簡單
    1|0理論知識周末上海下起了雨也降溫了,無事打開電腦看看源碼,就想到了線程池。線程池的技術網絡上已經有很多文章都已經寫過了,而且理論都是一樣的。但是理論歸理論,面試的時候也許你剛好看了一篇能應付過去,但是如果深究細節可能就會懵逼。