天天看點

【java線程系列】java線程系列之java線程池詳解

一線程池的概念及為何需要線程池:

我們知道當我們自己建立一個線程時如果該線程執行完任務後就進入死亡狀态,這樣如果我們需要在次使用一個線程時得重新建立一個線程,但是線程的建立是要付出一定的代價的,如果在我們的程式中需要頻繁使用線程,且每個線程執行的時間很短,短到幾乎小于線程建立及銷毀的時間那麼代價将會更大,如:伺服器應用程式中經常出現的情況是:單個任務處理的時間很短而請求的數目卻是巨大的。顯然如果頻繁的建立銷毀線程效率将非常低。

那麼我們能否讓一個線程可以複用,即當一個線程執行完後不銷毀該線程,而是讓其等待執行其它的任務.答案就是使用線程池。

何謂線程池:池線程池的基本思想還是一種對象池的思想,開辟一塊記憶體空間,裡面存放了衆多(未死亡)的線程,池中線程執行排程由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反複建立線程對象所帶來的性能開銷,節省了系統的資源。

合理的使用線程池相對于單獨使用線程的好處如下:

1 降低資源消耗,因為線程池減少了建立和銷毀線程的次數,每個工作線程都可以被重複利用,可執行多個任務。  

2 提高響應速度。因為線程池中的線程的建立是線程池管理器來建立的,當任務到達時,任務可以不需要等到線程建立就能立即執行。

3提高線程的可管理性,線程池為線程生命周期開銷問題和資源不足問題提供了解決方案,使用線程池可以對線程進行統一的配置設定,調優和監控。

二關于java線程池的幾個核心類

說到線程池首先我們得了解三個類:executors ,executorservice與threadpoolexecutor。其中executors 相當于一個建立線程池的工具類,而executorservice才是真正的線程池接口,而threadpoolexecutor是executorservice的具體實作類。我們一個一個來介紹:

1executors:建立線程池的工具類,在該類中提供了許多靜态方法來建立一個線程池,該類中的重要方法如下:

我們一個一個來看:

1 public static executorservice newfixedthreadpool(int nthreads)

通過傳入的int類型整數建立一個固定大小的線程池(creates a thread pool that reuses a fixed number of threads),每次送出一個任務就建立一個線程,當線程達到線程池的最大大小後送出的線程會在隊列中等待。

2 public static executorservice newsinglethreadexecutor()

 建立一個單線程的線程池(creates an executor that uses a single worker thread)。

3 public static executorservice newcachedthreadpool()

建立一個可以緩存的線程池,具體思想是當無線程空閑時建立一個新線程,否則重用先前建立的線程當先前建立的線程空閑時(creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.)

4 public static scheduledexecutorservice newscheduledthreadpool(int corepoolsize)

建立一個可以定時執行或周期性執行的線程池(creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.)

2executorservice:可以看到在上述介紹的executors的幾個重要方法的傳回值均為executorservice,我們先來看一下其類的定義:

可以看到executorservice是一個接口它繼承自executor(注意此處不是上面介紹的executors),那我們來看一下executor接口的定義:

可以看到executor接口代碼非常簡單僅僅包含一個void execute(runnable command);方法的聲明而已。也就是說executorservice接口繼承自executor接口,然後在此基礎上添加了一些自己的方法。

3threadpoolexecutor類:這個是建立一個線程的核心類,也是我們講解的重點,首先我們來看一下其類的定義:

可以看到threadpoolexecutor類繼承自abstractexecutorservice,那麼我們來看一下abstractexecutorservice類的定義:

可以看到abstractexecutorservice類是一個抽象類,它實作了executorservice,至于abstractexecutorservice類的内容,比較多我就不貼出來了,感興趣的可以去看一下源碼,讀者隻需要知道abstractexecutorservice類它實作了 executorservice接口中的絕大部分方法,部分方法未實作是以它是一個抽象類。

接下來看一下threadpoolexecutor類中的構造器。

可以看到threadpoolexecutor類為我們提供了四個構造器,其中第四個是最基本的構造器,其餘三個構造器均在其方法内調用了第四個構造器。是以我們重點講解第四個構造器的各參數的意義:

1 int corepoolsize:核心池的大小,是一個int型的參數,

2 int maximumpoolsize:線程池的最大線程數,是一個int型的參數,它表示線上程池中最多能建立多少個線程(the maximum number of threads to allow in the  pool)

3 long keepalivetime:表示無任務執行時線程最多元持多久後終止,是一個long類型的參數(this is the maximum time that excess idle threads will wait for new tasks before terminating.),隻有當線程池中的線程數大于corepoolsize時,keepalivetime才會起作用(when the number of threads is greater than  the core),直到線程池中的線程數不大于corepoolsize,即當線程池中的線程數大于corepoolsize時,如果一個線程空閑的時間達到keepalivetime,則會終止,直到線程池中的線程數不超過corepoolsize。

4 timeunit unit:參數keepalivetime的時間機關(the time unit for the {@code keepalivetime} argument)

5  blockingqueue<runnable> workqueue:阻塞隊列,用來存儲等待執行的任務(the queue to use for holding tasks before they are executed),這個隊列僅僅容納通過execute方法送出的runnable接口的任務(the queue to use for holding tasks before they are executed.  this queue will hold only the {@code runnable} 

tasks submitted by the {@code execute} method.)

6  threadfactory threadfactory:線程工廠,主要用來建立線程(the factory to use when the executor  creates a new thread)

7  rejectedexecutionhandler handler:它表示當一個線程的執行因為到達現場邊界且隊列容量達到極值而阻塞時(to use when execution is blocked because the thread           bounds and queue capacities are reached)而拒絕執行任務( rejectedexecution)時應該采取的政策。它的取值是一個 rejectedexecutionhandler 接口,取值供四種情    況:

threadpoolexecutor.abortpolicy:丢棄任務且抛出rejectedexecutionexception異常。 

threadpoolexecutor.discardpolicy:也是丢棄任務,但是不抛出異常。 

threadpoolexecutor.discardoldestpolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)

threadpoolexecutor.callerrunspolicy:由調用線程處理該任務

其中上述四個類都是threadpoolexecutor的靜态内部類,它們均實作了 rejectedexecutionhandler 接口,其類的定義如下:

接下來看一下threadpoolexecutor類的重要方法:

正如我們在上述介紹的,executor接口僅僅包含一個void execute(runnable command);方法的聲明,executorservice接口繼承自executor接口,然後在此基礎上添加了一些自己的方法。而abstractexecutorservice類它實作了executorservice接口中的絕大部分方法,少部分方法未實作(是以它是一個抽象類),而上述的幾個方法中的execute(),shutdown(),shutdownnow()在abstractexecutorservice類中未實作,它們是在threadpoolexecutor類中實作的,而submit是在abstractexecutorservice類中實作的。

注意execute()方法實際上是executor中聲明的方法,在threadpoolexecutor進行了具體的實作,這個方法是threadpoolexecutor的核心方法,通過這個方法可以向線程池送出一個任務,交給線程池去執行。

三java線程池的使用:

使用線程池時我們通常不是使用threadpoolexecutor這個核心類,而是使用executors這個工具類中的幾個靜态方法,

這幾個方法的使用差不多,是以我們以建立固定大小的線程池executors.newfixedthreadpool(int);這個方法為例來講解線程池的使用,我打算以伺服器端使用線程池來連接配接用戶端的socket請求通信為例來講解其使用,代碼如下:

從上述代碼示例可以看出,線程池的使用步驟如下:

1使用executors這個工具類中的幾個靜态方法建立一個threadpoolexecutor對象,如

注意幾個靜态方法傳回的是其父類executorservice接口,通常我們在指定線程的個數時不直接指定為一個固定值,而是使用類似runtime.getruntime.availableprocessors() * 50的方式充分利用多核計算機的性能,

2建立一個實作了runnable接口的線程,如:

重寫其run方法,在run方法中完成自己的業務邏輯。

 3調用executorservice對象的execute()方法執行2中建立的runnable對象,該方法的參數是一個runnable對象,如:

注意execute()方法實際上是executor中聲明的方法,在threadpoolexecutor進行了具體的實作。

好了以上就是本人了解的關于java線程池的内容,看官如果覺得不錯,請記得點選下方的”頂“或贊給我一點鼓勵哦!

【java線程系列】java線程系列之java線程池詳解