天天看點

線程池

池化技術應用:線程池、資料庫連接配接池、http連接配接池等等。

池化技術的思想主要是為了減少每次擷取資源的消耗,提高對資源的使用率。

線程池提供了一種限制、管理資源的政策。 每個線程池還維護一些基本統計資訊,例如已完成任務的數量。

使用線程池的好處:

降低資源消耗:通過重複利用已建立的線程降低線程建立和銷毀造成的消耗。

提高響應速度:當任務到達時,可以不需要等待線程建立就能立即執行。

提高線程的可管理性:線程是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的配置設定,監控和調優。

executor架構不僅包括了線程池的管理,還提供了線程工廠、隊列以及拒絕政策等,讓并發程式設計變得更加簡單。

executor架構的使用示意圖

線程池

主線程首先要建立實作runnable或callable接口的任務對象。

把建立完成的實作runnable/callable接口的對象直接交給executorservice執行:

如果執行executorservice.submit(...),executorservice将傳回一個實作future接口的對象。 最後,主線程可以執行futuretask.get()方法來等待任務執行完成。主線程也可以執行futuretask.cancel()來取消次任務的執行。

線程池分析原理

線程池

java.uitl.concurrent.threadpoolexecutor類是線程池中最核心的一個類,是以如果要透徹地了解java中的線程池,必須先了解這個類。下面我們來看一下threadpoolexecutor類的具體實作源碼。

在threadpoolexecutor類中提供了四個構造方法:

從上面的代碼可以得知,threadpoolexecutor繼承了abstractexecutorservice類,并提供了四個構造器,事實上,通過觀察每個構造器的源碼具體實作,發現前面三個構造器都是調用的第四個構造器進行的初始化工作。

線程池構造方法參數:

corepoolsize:線程池中核心線程的數量,當線程池中的線程數目達到corepoolsize後,就會把到達的任務放到任務隊列中

maximumpoolsize:線程池中最大線程數量,隊列填滿之後在核心線程數的基礎上,額外增加的線程數的上限,如果是無界隊列,因為無界隊列不會被填滿是以此參數不會起作用

keepalivetime:非核心線程的逾時時長,當系統中非核心線程閑置時間超過keepalivetime之後,則會被回收。如果threadpoolexecutor的allowcorethreadtimeout屬性設定為true,則該參數也表示核心線程的逾時時長

unit:keepalivetime 參數的機關,有納秒、微秒、毫秒、秒、分、時、天等,在timeunit類中有對應7種靜态屬性

workqueue:線程池中的任務隊列,該隊列主要用來存儲已經被送出但是尚未執行的任務。存儲在這裡的任務是由threadpoolexecutor的execute方法送出來的。

threadfactory:為線程池提供建立新線程的功能,這個我們一般使用預設即可

handler:拒絕政策,當線程無法執行新任務時(一般是由于線程池中的線程數量已經達到最大數或者線程池關閉導緻的),預設情況下,當線程池無法處理新線程時,會抛出一個rejectedexecutionexception。

1.手動建立線程池

2.自動建立,由jdk提供已經設定固定參數的線程池

(1)fixedthreadpool:   executors.newfixedthreadpool(); 

fixedthreadpool是一個核心線程數量固定的線程池,核心線程數和最大線程數一樣,說明在fixedthreadpool中沒有非核心線程,所有的線程都是核心線程,且線程的逾時時間為0,說明核心線程即使在沒有任務可執行的時候也不會被銷毀(這樣可讓fixedthreadpool更快速的響應請求),最後的線程隊列是一個linkedblockingqueue,但是linkedblockingqueue卻沒有參數,這說明線程隊列的大小為integer.max_value(2的31次方減1),看完參數,我們大概也就知道了fixedthreadpool的工作特點了,當所有的核心線程都在執行任務的時候,新的任務隻能進入線程隊列中進行等待,直到有線程被空閑出來

(2)singlethreadpool:  executors.newsinglethreadexecutor();

singlethreadexecutor和fixedthreadpool很像,不同的就是singlethreadexecutor的核心線程數隻有1,使用singlethreadexecutor的一個最大好處就是可以避免我們去處理線程同步問題,其實如果我們把fixedthreadpool的參數傳個1,效果就和singlethreadexecutor一緻了

(3)cachedthreadpool:  executors.newcachedthreadpool();

cachedthreadpool中是沒有核心線程的,但是它的最大線程數卻為integer.max_value,另外,它是有線程逾時機制的,逾時時間為60秒,這裡它使用了synchronousqueue作為線程隊列,synchronousqueue的特點上文已經說過了,這裡不再贅述。那麼我們送出到cachedthreadpool消息隊列中的任務在執行的過程中有什麼特點呢?由于最大線程數為無限大,是以每當我們添加一個新任務進來的時候,如果線程池中有空閑的線程,則由該空閑的線程執行新任務,如果沒有空閑線程,則建立新線程來執行任務。根據cachedthreadpool的特點,我們可以在有大量任務請求的時候使用cachedthreadpool,因為當cachedthreadpool中沒有新任務的時候,它裡邊所有的線程都會因為逾時而被終止

(4)scheduledthreadpool:  executors.newscheduledthreadpool();

scheduledthreadpool是一個具有定時定期執行任務功能的線程池。它的核心線程數量是固定的(我們在構造的時候傳入的),但是非核心線程是無窮大,當非核心線程閑置時,則會被立即回收

private final reentrantlock lock = new reentrantlock();

lock.lock();    加鎖

lock.unlock();   釋放鎖

1.shutdown()  關閉線程池,不影響已經送出的任務

2.shutdownnow() 關閉線程池,并嘗試去終止正在執行的線程

3.allowcorethreadtimeout(boolean value) 允許核心線程閑置逾時時被回收

4.submit 一般情況下我們使用execute來送出任務,但是有時候可能也會用到submit,使用submit的好處是submit有傳回值

使用submit時我們可以通過實作callable接口來實作異步任務。在call方法中執行異步任務,傳回值即為該任務的傳回值。future是傳回結果,傳回它的isdone屬性表示異步任務執行成功!