Java中的線程池是運用場景最多的并發(fā)框架戴已,幾乎所有需要異步或并發(fā)執(zhí)行任務的程序都可以使用線程池艇拍。在開發(fā)過程中私爷,合理使用線程池能夠帶來3個好處:
(1)降低資源消耗。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗斗躏。
(2)提高響應速度逝慧。當任務到達時,任務可以不需要等到線程創(chuàng)建就能立刻執(zhí)行啄糙。
(3)提高線程的可管理性笛臣。線程是稀缺資源,如果無限制的創(chuàng)建隧饼,不僅會消耗系統(tǒng)資源沈堡,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一分配桑李,調(diào)優(yōu)和監(jiān)控踱蛀。
9.1 線程池的實現(xiàn)原理
當提交一個新任務到線程池時,線程池的處理流程如下:
(1)線程池判斷核心線程池是否已滿(線程數(shù)量是否小于corePoolSize)贵白。如果核心線程池已滿,則創(chuàng)建一個新的線程執(zhí)行任務崩泡;否則進入下個流程禁荒。
(2)線程池判斷工作隊列(BlockingQueue)是否已滿。如果工作隊列未滿角撞,則將新提交的任務存儲在工作隊列中呛伴。否則進入下個流程。
(3)線程池判斷線程池是否已滿(線程數(shù)量是否小于maximumPoolSize)谒所。如果線程池未滿热康,則創(chuàng)建新的線程執(zhí)行這個任務。如果線程池已滿劣领,則交給飽和策略來處理這個任務姐军。
工作線程:線程池創(chuàng)建線程時,會將線程封裝成工作線程Worker尖淘,worker在執(zhí)行完任務后奕锌,會反復從BlockingQueue中獲取任務來執(zhí)行。
9.2 線程池的使用
public ThreadPoolExecutor(int corePoolSize村生, int maximumPoolSize惊暴, long keepAliveTime,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit unit, BlockingQueue<Runnable> workQueue趁桃,ThreadFactory? threadFactory辽话,RejectedExecutionHandler handler)肄鸽;
創(chuàng)建一個線程池需要以下幾個參數(shù):
(1)corePoolSize:線程池的基本大小。當提交一個任務到線程池時油啤,即使存在空閑的線程贴捡,線程池也會創(chuàng)建一個新的線程來執(zhí)行任務,除非當前線程池的線程數(shù)量已經(jīng)超過corePoolSize村砂。如果調(diào)用了prestartAllCoreThreads()方法烂斋,線程池會提前創(chuàng)建并啟動所有的基本線程。
(2)BlockingQueue<Runnable>?workQueue:用于保存等待執(zhí)行的任務的阻塞隊列础废⊙绰睿可以選擇如下幾個阻塞隊列:
ArrayBlockingQueue:是一個基于數(shù)組的有界阻塞隊列。按FIFO對元素進行排序评腺。
(2.1)LinkedBlockingQueue:基于鏈表的阻塞隊列帘瞭。按FIFO對元素進行排序。吞吐量通常高于(2.2)ArrayBlockingQueue蒿讥。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個隊列蝶念。
(2.3)SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等待另一個線程調(diào)用移除操作芋绸,否則插入操作一直處于阻塞狀態(tài)媒殉。吞吐量通常高于LinkedBlockingQueue。靜態(tài)工廠方法Executors.newCachedThreadPool()使用了這個隊列摔敛。
(2.4)PriorityBlockingQueue:一個具有優(yōu)先級的無限阻塞隊列廷蓉。
(3)maximumPoolSize:線程池允許創(chuàng)建的最大線程數(shù)量。如果隊列已滿马昙,并且創(chuàng)建的線程數(shù)量小于maximumPoolSize桃犬,則線程池會再創(chuàng)建新的線程執(zhí)行任務。值得注意的是行楞,如果使用了無界隊列攒暇,那么這個參數(shù)就沒有什么意義。
(4)ThreadFactory:用于設置創(chuàng)建線程的工廠子房。ThreadFactory是一個擁有一個方法的接口:Thread newThread (Runnable r);
(5)RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了形用,說明線程池處于飽和狀態(tài),那么必須采取一種策略處理新提交的任務池颈。這個策略默認是AbortPolicy尾序。JDK1.5提供了如下四種策略:
? ? ? ? ? ? ? ? ?AbortPolicy:直接拋出異常。
? ? ? ? ? ? ? ? CallerRunsPolicy:使用調(diào)用者所在的線程來運行任務躯砰。
? ? ? ? ? ? ? ? DiscardOldestPolicy:丟棄掉隊列里最近的一個任務每币,并執(zhí)行當前任務。
? ? ? ? ? ? ? ? DiscardPolicy:不處理琢歇,丟棄掉兰怠。
當然也可以根據(jù)應用場景來實現(xiàn)RejectedExceptionHandler幾口自定義策略梦鉴。如記錄日志或者持久化不能存儲的任務。
(6)keepAliveTime(多余線程保持活動的時間):當線程池線程數(shù)量大于corePoolSize時揭保,線程池的工作線程空閑后肥橙,保持存活的時間。
(7)TimeUnit:線程保持活動時間的單位秸侣。
9.2.2 向線程池提交任務
可以使用兩個方法向線程池提交任務:execute(Runnable command)和submit(Runnable task)
execute()方法用于提交不需要返回值的任務存筏。
submit()方法用于提交需要返回值的任務。線程池會返回一個Future<T>類型的對象味榛,并且通過future的get()方法來獲取返回值椭坚,get()方法會阻塞當前線程知道任務返回。
9.2.3 關(guān)閉線程池
可以通過shuntdown()或者shutdownNow()方法來關(guān)閉線程池搏色。它們的原理是遍歷線程池中的工作線程善茎,然后調(diào)用interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法停止频轿。
shutdown()方法將線程池的狀態(tài)設為SHUTDOWN狀態(tài)垂涯,可以達到拒絕任何新的任務的效果。但是不會對已經(jīng)提交了的任務造成影響航邢。
shutdownNow()方法在shutdown()方法的基礎上耕赘,會嘗試中斷已經(jīng)提交了的任務。如果任務忽略中斷翠忠,那么效果和shutdown()一樣鞠苟。
docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html