java併發程式設計學習11 執行緒池

2021-08-10 03:48:41 字數 3915 閱讀 8986

【執行緒池

為了節省系統在多執行緒併發時不斷建立和銷毀執行緒帶來的額外開銷,就需要引入執行緒池。執行緒池的基本功能就是進行執行緒的復用。當系統接受一

個提交的任務時,並不會著急去建立乙個新的執行緒去執行這個任務,而是去執行緒池中查詢是否有空閒的執行緒。

若有:直接使用這個執行緒。

若沒有:根據配置的策略執行(有可能時建立乙個新的執行緒,也有可能是阻塞該任務等待空閒執行緒)。

待任務結束之後,也不會銷毀執行緒,而是放入執行緒池的空閒佇列,等待下次使用。

【executor框架

為了能更好的控制多執行緒,jdk提供了一套executor框架。其中threadpoolexecutor表示乙個執行緒池。executors表示乙個執行緒工廠,通過executors可以取得乙個特定功能的執行緒池。

public static executorservice newfixedthreadpool(int nthreads)

建立固定數目執行緒的執行緒池。

public static executorservice newcachedthreadpool()

建立乙個可快取的執行緒池,呼叫execute 將重用以前構造的執行緒(如果執行緒可用)。如果現有執行緒沒有可用的,則建立乙個新執行緒並新增到池中。終止並從快取中移除那些已有 60 秒鐘未被使用的執行緒。

public static executorservice newsinglethreadexecutor()

建立乙個單執行緒化的executor。

public static scheduledexecutorservice newscheduledthreadpool(int corepoolsize)

建立乙個支援定時及週期性的任務執行的執行緒池,多數情況下可用來替代timer類。

但是這些工廠方法最後都是使用的threadpoolexecutor這個類。

【threadpoolexecutor引數含義:

corepoolsize:核心池的大小,這個引數跟後面講述的執行緒池的實現原理有非常大的關係。在建立了執行緒池後,預設情況下,執行緒池中並沒有任何執行緒,而是等待有任務到來才建立執行緒去執行任務,除非呼叫prestartallcorethreads()或者prestartcorethread()方法。從這2個方法的名字就可以看出,是預建立執行緒的意思,即在沒有任務到來之前就建立corepoolsize個執行緒或者乙個執行緒。預設情況下,在建立了執行緒池後,執行緒池中的執行緒數為0。當有任務來之後,就會建立乙個執行緒去執行任務,當執行緒池中的執行緒數目達到corepoolsize後,就會把到達的任務放到快取佇列當中;

maximumpoolsize

keepalivetime:表示執行緒沒有任務執行時最多保持多久時間會終止。預設情況下,只有當執行緒池中的執行緒數大於corepoolsize時,keepalivetime才會起作用。

直到執行緒池中的執行緒數不大於corepoolsize,即當執行緒池中的執行緒數大於corepoolsize時,如果乙個執行緒空閒的時間達到keepalivetime,則會終止,直到執行緒池中的執行緒數不超過corepoolsize。

unit:引數keepalivetime的時間單位

workqueue:乙個阻塞佇列,用來儲存等待執行的任務,這個引數的選擇也很重要,會對執行緒池的執行過程產生重大影響,一般來說,這裡的阻塞佇列有以下幾種選擇:

arrayblockingqueue;linkedblockingqueue;synchronousqueue;

threadfactory:執行緒工廠,主要用來建立執行緒;

handler:表示當拒絕處理任務時的策略,有以下四種取值:

threadpoolexecutor.abortpolicy:丟棄任務並丟擲    rejectedexecutionexception異常。

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

threadpoolexecutor.discardoldestpolicy:丟棄佇列最前面的       任務,然後重新嘗試執行任務(重複此過程)                      threadpoolexecutor.callerrunspolicy:由呼叫執行緒處理該任務

【現在重點講講workqueue:

引數workqueue是指被提交但未執行的任務佇列,他是乙個blockingqueue介面的物件,僅用於存放runnable物件。根據佇列功能分類,在threadpoolexecutor構造引數中可以使用以下幾種blockingqueue:

1.直接提交佇列:

synchronousqueue物件提供。synchronousqueue是乙個特殊的blockingqueue,synchronousqueue沒有容量,不儲存任務他總是將任務提交給執行緒執行,如果沒有空閒的執行緒就會嘗試建立新的執行緒。如果執行緒數達到maximumpoolsize,則執行拒絕策略。因此使用synchronousqueue通常會設定很大的maximumpoolsize,否則容易執行拒絕策略。

2.有界任務佇列:

3.無界任務佇列:

無界任務佇列可以通過linkedblockingqueue類實現。與有界佇列相比,除非系統資源耗盡,否則無界任務佇列不存在任務入隊失敗的情況。當有新任務提交的時候:如果系統執行緒小於corepoolsize,會建立新的執行緒執行任務,如果大於corepoolsize則不會增加新的執行緒,任務會進入等待佇列,無界佇列會持續增長,直到系統資源耗盡。

4.優先任務佇列:

帶有執行優先順序的佇列,通過priorityblockingqueue實現,可以控制任務執行順序特殊的無界佇列。無論有界還是無界佇列,都是fifo的執行任務,但是優先順序任務佇列可以根據任務自身的優先順序決定任務執行順序,在確保了效能的同時,也能保證執行質量(高優先順序任務先執行)。

【執行緒池如何復用執行緒

我們知道執行緒池會復用執行緒,但是它的內部邏輯是如何將乙個runnable物件賦值給thread的呢?

1.執行緒池內部維護的不是thread物件而是乙個內部類worker:

它繼承了abstractqueuedsynchronizer類,實現了乙個非重入的鎖。該鎖會保護乙個正在等待任務被執行的worker不被interrupt操作打斷。為什麼不用reentrantlock,要用非重入的鎖?因為作者不想讓這個worker task在setcorepoolsize這種執行緒池控制方法呼叫時能重新獲取到鎖。當提交乙個任務時,如果需要建立乙個執行緒(何時需要在下一節中**)時,就呼叫執行緒工廠建立乙個執行緒,同時將執行緒繫結到worker工作佇列中。需要說明的是,worker佇列構造的時候帶著乙個任務runnable,因此worker建立時總是繫結著乙個待執行任務。換句話說,建立執行緒的前提是有必要建立執行緒,不會無緣無故建立一堆空閒執行緒等著任務。這是節省資源的一種方式。

2.執行緒重用:

執行緒重用的核心是,它把thread.start()給遮蔽起來了(一定不要重複呼叫),然後它自己有乙個runnable.run(),迴圈在跑,跑的過程中不斷檢查我們是否有新加入的子runnable物件,有就調一下我們的run(),其實就乙個大run()把其它小run()#1,run()#2,...給串聯起來了,基本原理就這麼簡單。 

java併發程式設計 執行緒池

降低資源消耗 提高響應速度 t1 執行緒建立的時間 t2 工作任務執行額時間 t3 執行緒銷毀時間 提高了執行緒的可管理性 corepoolsize 核心執行緒數 如果執行的執行緒數大於核心執行緒數,則會先進入到阻塞佇列裡 maxinumpoolsize 允許最大執行緒數 執行緒池所建立的執行緒數一...

Java 執行緒池併發程式設計詳解

本博文分為6個部分 1.bizprocessor 具體的事務處理邏輯,被多執行緒呼叫。2.bizasynctaskcall 實現callable介面,用於組裝futuretask。3.bizfuturetask 代表乙個非同步計算任務,用於提交任務非同步執行,並返回計算結果。4.bizexecuto...

Java併發程式設計 執行緒池 例項

public class test executor.shutdown while true try catch interruptedexception e public void testrun threadpoolexecutor executor,final int a catch inte...