使用多線程,可以把一些大任務分解成多個小任務來執行,多個小任務之間互不影像,同時進行,這樣,充分利用了cpu資源。
繼承Thread類,重寫run方法;
實作Runable接口,實作run方法;
建立:當new了一個線程,并沒有調用start之前,線程處于建立狀态;
就緒:當調用了start之後,線程處于就緒狀态,這是,線程排程程式還沒有設定執行目前線程;
運作:線程排程程式執行到線程時,目前線程從就緒狀态轉成運作狀态,開始執行run方法裡邊的代碼;
阻塞:線程在運作的時候,被暫停執行(通常等待某項資源就緒後在執行,sleep、wait可以導緻線程阻塞),這是該線程處于阻塞狀态;
死亡:當一個線程執行完run方法裡邊的代碼或調用了stop方法後,該線程結束運作
當我們需要的并發執行線程數量很多時,且每個線程執行很短的時間就結束了,這樣,我們頻繁的建立、銷毀線程就大大降低了工作效率(建立和銷毀線程需要時間、資源)。
java中的線程池可以達到這樣的效果:一個線程執行完任務之後,繼續去執行下一個任務,不被銷毀,這樣線程使用率提高了。
說起java中的線程池,就想到java.util.concurrent.ThreadPoolExecutor。ThreadPoolExecutor類是java線程池中的核心類。他的實作方式有四種:
通過ThreadPoolExecutor類的源碼可以看出,ThreadPoolExecutor類繼承AbstractExecutorService,提供四個構造方法,通過構造方法可以看出前面三個最終調了最後一個。
下面介紹下構造方法中的參數:
corePoolSize:線程池的大小。線程池建立之後不會立即去建立線程,而是等待線程的到來。當目前執行的線程數大于改值是,線程會加入到緩沖隊列。
maximumPoolSize:線程池中建立的最大線程數;
keepAliveTime:空閑的線程多久時間後被銷毀。預設情況下,改值線上程數大于corePoolSize時,對超出corePoolSize值得這些線程起作用。
unit:TimeUnit枚舉類型的值,代表keepAliveTime時間機關,可以取下列值:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //納秒
workQueue:阻塞隊列,用來存儲等待執行的任務,決定了線程池的排隊政策,有以下取值:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
threadFactory:線程工廠,是用來建立線程的。預設new Executors.DefaultThreadFactory();
handler:線程拒絕政策。當建立的線程超出maximumPoolSize,且緩沖隊列已滿時,新任務會拒絕,有以下取值:
ThreadPoolExecutor.AbortPolicy:丢棄任務并抛出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丢棄任務,但是不抛出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
以下是具體的實作方式:
以下是ThreadPoolExecutor具體的繼承結構:
這是一個抽象類,實作了ExecutorService接口,并實作了ExecutorService裡邊的方法。
下面看下ExecutorService接口的具體實作:
ExecutorService繼承Executor接口,下面是Executor接口的具體實作:
Executor接口是頂層接口,隻聲明了一個execute方法,該方法是用來執行傳遞進來的任務的。
回過頭來,咱麼重新看ThreadPoolExecutor類,改類裡邊有以下兩個重要的方法:
execute()方法是Executor中聲明的方法,在ThreadPoolExecutor有了具體的實作,這個方法是ThreadPoolExecutor的核心方法,
通過這個方法可以向線程池送出一個任務,交由線程池去執行。
submit()方法是ExecutorService中聲明的方法,在AbstractExecutorService中進行了實作,Executor中并沒有對其進行重寫。從實作中可以看出,submit方法最終也調用了execute方法,也是執行一個人去,但submit方法可以傳回執行結果,利用Future來擷取任務執行結果。
Spring中的線程池是由ThreadPoolTaskExecutor類來實作的。該類的實作原理最終也是調用了java中的ThreadPoolExecutor類中的一些方法。具體的實作讀者可以自己去翻閱Spring的源碼,這裡筆者就不羅列了。我們看下ThreadPoolTaskExecutor的初化。
ThreadPoolTaskExecutor有兩種常用的有兩種初始化方式:xml配置,java代碼初始化。
xml配置:
看過上面的内容,讀者應該很清楚上面的一些參數代表的意思了吧。筆者在這裡不一一去解釋了。
Java代碼初始化: