­

java 線程池簡介

  • 2019 年 10 月 25 日
  • 筆記

版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/qq_32534855/article/details/99608297

簡介

什麼是「池」 : 軟件中的「池」,可以理解為計劃經濟時代的工廠。 首先,作為工廠,你要管理好你生產的東西,老王從你工廠這裡拿走了一把斧頭,改天他不需要了,還回來,你可以把這把斧頭借給老趙; 其次,你又不能無限制的生產,畢竟在資源極度匱乏的時代,如果都被你拿去生產了,其他要用到資源的地方怎麼辦? 總結成兩點,「池」的作用: 復用已有資源 控制資源總量 數據庫連接池是這樣,線程池也是如此。 你一個任務過來了,我發現池子里有沒事幹並且還活着的線程,來,拿去用,我也不用費事給你創建一條線程了,要知道線程的創建和銷毀可都是麻煩事; 你一個任務過來了,我發現池子的線程都在忙,並且現在池子的線程已經太多了,再不限制下去就要內存溢出了,來,排隊去~

Java中常見的4種線程池:

1    FixedThreadPool 2    CachedThreadPool 3    ScheduledThreadPool 4    SingleThreadExecutor

FixedThreadPool: 固定線程數的線程池,可控制線程最大並發數,超出的線程會在隊列中等待。

這裡的線程數是固定的,每個線程都會從任務隊列中去取任務,由於這個取的動作是並發的,所以要求任務隊列具有線程安全的特性,所以通常會使用阻塞隊列來實現。

CachedThreadPool: 可緩存線程池(特點: 無界線程池,具有自動回收多餘線程的功能)

在這種情況下,我們的線程數量並不是固定的,而且我們也沒有一個用來存儲已提交任務的隊列;相反,這種線程池用有一個同步隊列(synchronous queue),它只能存儲1個Task。每當我們提交了一個任務給線程池,這個任務就會被放到這個隊列中。此時,線程池會在已經創建的所有線程中去尋找空閑的線程,並把這一個任務交給這個空閑的線程去執行。如果線程池沒能找到一個空閑的線程,說明所有的線程都在工作,那麼它就會新建一個線程,並把這個任務交給這個剛剛創建好的線程去執行,這樣就達到了動態增加線程數量的目的。

ScheduledThreadPool 支持定時及周期性任務執行的線程池。

這種線程池是專門服務於需要延遲或周期執行任務的場景,例如我們需要每10秒進行一次安全檢查。  ScheduledThreadPool主要有3種執行任務的方法:  1.    service.schedule 延遲且單次執行,例如10秒後執行,總共只執行一次  2.    service.scheduleAtFixedRate 每間隔固定時間執行一次,例如每10秒執行一次  3.    service.scheduleAtFixedDelay 在上一個任務執行後,間隔固定時間再次執行。例如:每個Task需要執行5分鐘,那麼我們可以設定在這個Task直接結束後,等待1分鐘再執行下一次的任務,所以最終的效果就是每6分鐘該任務會被執行一次。  scheduleAtFixedRate和scheduleAtFixedDelay的區別在於,scheduleAtFixedRate是以任務開始執行的時間為起點,開始計時,而scheduleAtFixedDelay是以任務執行完畢為時間起點進行計時。

這裡用來存儲Task的queue是一個延遲隊列,延遲隊列的特點是:不是先進先出,而是會按照延遲時間的長短來排序,下一個即將執行的任務會排到隊列的最前面。我們來舉個例子:例如我們往這個隊列中,放一個延遲10分鐘執行的任務,然後再放一個延遲10秒鐘執行的任務。通常而言,如果不是延遲隊列而是普通隊列,那麼按照先進先出的排列規則,也就是第一個放置的會排在最前面,也就是延遲10分鐘執行的那個任務會放在最前面,但是由於我們此時使用的是延遲隊列,延遲隊列在排放各個任務的位置的時候,會根據延遲時間的長短來排放。所以。我們第二個放置的延遲10秒鐘執行的那個任務,反而會排在延遲10分鐘的任務的前面,因為它的執行時間更早。

SingleThreadExecutor 單線程的線程池:它只會用唯一的工作線程來執行任務,它的原理和FixedThreadPool是一樣的,但是此時的線程數量被設置為了1。這裡的這一個線程,會不停地從任務隊列里取任務,然後執行。如果線程在執行任務的時候,遇到了異常導致線程執行中斷,那麼線程池會重新創建一個線程繼續執行後續的任務,而不會永遠地中斷執行任務。

使用場景:目的是保證所有任務按照提交任務的順序執行。例如我們想要讓第二個任務必須在第一個任務之後執行,同理,或者我們希望讓任務三在任務二之後執行,這種場景下就需要用到SingleThreadExecutor。因為執行任務的線程只有一個,所以我們可以保證執行的順序,而之前那幾種線程池在執行的時候,由於有多個線程同時執行,所以我們不能保證各個任務之間的執行順序。

以上4種線程池的構造函數的參數: 其中fixedThreadPool的keepAliveTime是0秒,這裡的0代表永久,也就是fixedThreadPool不會kill任何一個idle的線程,即便該線程無事可做(不執行Task)。 newCachedThreadPool的參數指定了corePoolSize為0,所以CachedThreadPool在最開始的時候並不會創建線程,直到有Task進來,需要線程工作的時候才會創建線程。而可以看出CachedThreadPool的maxPoolSize的值是Integer.MAX_VALUE,這可能會創建數量非常多的線程,甚至導致OOM。 ScheduledThreadPool和SingleThreadPool也是同理。