1、線程池的優(yōu)勢
(1)、降低系統(tǒng)資源消耗,通過重用已存在的線程,降低線程創(chuàng)建和銷毀造成的消耗;
(2)、提高系統(tǒng)響應(yīng)速度,當(dāng)有任務(wù)到達(dá)時,通過復(fù)用已存在的線程,無需等待新線程的創(chuàng)建便能立即執(zhí)行;
(3)方便線程并發(fā)數(shù)的管控。因為線程若是無限制的創(chuàng)建,可能會導(dǎo)致內(nèi)存占用過多而產(chǎn)生OOM,并且會造成cpu過度切換(cpu切換線程是有時間成本的(需要保持當(dāng)前執(zhí)行線程的現(xiàn)場,并恢復(fù)要執(zhí)行線程的現(xiàn)場))。
(4)提供更強(qiáng)大的功能,延時定時線程池。
2、線程池的主要參數(shù)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1、corePoolSize(線程池基本大?。寒?dāng)向線程池提交一個任務(wù)時,若線程池已創(chuàng)建的線程數(shù)小于corePoolSize,即便此時存在空閑線程,也會通過創(chuàng)建一個新線程來執(zhí)行該任務(wù),直到已創(chuàng)建的線程數(shù)大于或等于corePoolSize時,(除了利用提交新任務(wù)來創(chuàng)建和啟動線程(按需構(gòu)造),也可以通過 prestartCoreThread() 或 prestartAllCoreThreads() 方法來提前啟動線程池中的基本線程。)
2、maximumPoolSize(線程池最大大?。壕€程池所允許的最大線程個數(shù)。當(dāng)隊列滿了,且已創(chuàng)建的線程數(shù)小于maximumPoolSize,則線程池會創(chuàng)建新的線程來執(zhí)行任務(wù)。另外,對于無界隊列,可忽略該參數(shù)。
3、keepAliveTime(線程存活保持時間)當(dāng)線程池中線程數(shù)大于核心線程數(shù)時,線程的空閑時間如果超過線程存活時間,那么這個線程就會被銷毀,直到線程池中的線程數(shù)小于等于核心線程數(shù)。
4、workQueue(任務(wù)隊列):用于傳輸和保存等待執(zhí)行任務(wù)的阻塞隊列。
5、threadFactory(線程工廠):用于創(chuàng)建新線程。threadFactory創(chuàng)建的線程也是采用new Thread()方式,threadFactory創(chuàng)建的線程名都具有統(tǒng)一的風(fēng)格:pool-m-thread-n(m為線程池的編號,n為線程池內(nèi)的線程編號)。
5、handler(線程飽和策略):當(dāng)線程池和隊列都滿了,再加入線程會執(zhí)行此策略。
3、線程池流程

1、判斷核心線程池是否已滿,沒滿則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。已滿則。
2、判斷任務(wù)隊列是否已滿,沒滿則將新提交的任務(wù)添加在工作隊列,已滿則。
3、判斷整個線程池是否已滿,沒滿則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù),已滿則執(zhí)行飽和策略。
(1、判斷線程池中當(dāng)前線程數(shù)是否大于核心線程數(shù),如果小于,在創(chuàng)建一個新的線程來執(zhí)行任務(wù),如果大于則
2、判斷任務(wù)隊列是否已滿,沒滿則將新提交的任務(wù)添加在工作隊列,已滿則。
3、判斷線程池中當(dāng)前線程數(shù)是否大于最大線程數(shù),如果小于,則創(chuàng)建一個新的線程來執(zhí)行任務(wù),如果大于,則執(zhí)行飽和策略。)
4、線程池為什么需要使用(阻塞)隊列?
回到了非線程池缺點中的第3點:
1、因為線程若是無限制的創(chuàng)建,可能會導(dǎo)致內(nèi)存占用過多而產(chǎn)生OOM,并且會造成cpu過度切換。
另外回到了非線程池缺點中的第1點:
2、創(chuàng)建線程的消耗較高。
或者下面這個網(wǎng)上并不高明的回答:
2、線程池創(chuàng)建線程需要獲取mainlock這個全局鎖,影響并發(fā)效率,阻塞隊列可以很好的緩沖。
5、線程池為什么要使用阻塞隊列而不使用非阻塞隊列?
阻塞隊列可以保證任務(wù)隊列中沒有任務(wù)時阻塞獲取任務(wù)的線程,使得線程進(jìn)入wait狀態(tài),釋放cpu資源。
當(dāng)隊列中有任務(wù)時才喚醒對應(yīng)線程從隊列中取出消息進(jìn)行執(zhí)行。
使得在線程不至于一直占用cpu資源。
(線程執(zhí)行完任務(wù)后通過循環(huán)再次從任務(wù)隊列中取出任務(wù)進(jìn)行執(zhí)行,代碼片段如下
while (task != null || (task = getTask()) != null) {})。
不用阻塞隊列也是可以的,不過實現(xiàn)起來比較麻煩而已,有好用的為啥不用呢?
6、如何配置線程池
CPU密集型任務(wù)
盡量使用較小的線程池,一般為CPU核心數(shù)+1。 因為CPU密集型任務(wù)使得CPU使用率很高,若開過多的線程數(shù),會造成CPU過度切換。
IO密集型任務(wù)
可以使用稍大的線程池,一般為2*CPU核心數(shù)。 IO密集型任務(wù)CPU使用率并不高,因此可以讓CPU在等待IO的時候有其他線程去處理別的任務(wù),充分利用CPU時間。
混合型任務(wù)
可以將任務(wù)分成IO密集型和CPU密集型任務(wù),然后分別用不同的線程池去處理。 只要分完之后兩個任務(wù)的執(zhí)行時間相差不大,那么就會比串行執(zhí)行來的高效。
因為如果劃分之后兩個任務(wù)執(zhí)行時間有數(shù)據(jù)級的差距,那么拆分沒有意義。
因為先執(zhí)行完的任務(wù)就要等后執(zhí)行完的任務(wù),最終的時間仍然取決于后執(zhí)行完的任務(wù),而且還要加上任務(wù)拆分與合并的開銷,得不償失。
7、java中提供的線程池
Executors類提供了4種不同的線程池:newCachedThreadPool, newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor

1、newCachedThreadPool:用來創(chuàng)建一個可以無限擴(kuò)大的線程池,適用于負(fù)載較輕的場景,執(zhí)行短期異步任務(wù)。(可以使得任務(wù)快速得到執(zhí)行,因為任務(wù)時間執(zhí)行短,可以很快結(jié)束,也不會造成cpu過度切換)
2、newFixedThreadPool:創(chuàng)建一個固定大小的線程池,因為采用無界的阻塞隊列,所以實際線程數(shù)量永遠(yuǎn)不會變化,適用于負(fù)載較重的場景,對當(dāng)前線程數(shù)量進(jìn)行限制。(保證線程數(shù)可控,不會造成線程過多,導(dǎo)致系統(tǒng)負(fù)載更為嚴(yán)重)
3、newSingleThreadExecutor:創(chuàng)建一個單線程的線程池,適用于需要保證順序執(zhí)行各個任務(wù)。
4、newScheduledThreadPool:適用于執(zhí)行延時或者周期性任務(wù)。
8、execute()和submit()方法
1、execute(),執(zhí)行一個任務(wù),沒有返回值。
2、submit(),提交一個線程任務(wù),有返回值。
submit(Callable<T> task)能獲取到它的返回值,通過future.get()獲?。ㄗ枞钡饺蝿?wù)執(zhí)行完)。一般使用FutureTask+Callable配合使用(IntentService中有體現(xiàn))。
submit(Runnable task, T result)能通過傳入的載體result間接獲得線程的返回值。
submit(Runnable task)則是沒有返回值的,就算獲取它的返回值也是null。
Future.get方法會使取結(jié)果的線程進(jìn)入阻塞狀態(tài),知道線程執(zhí)行完成之后,喚醒取結(jié)果的線程,然后返回結(jié)果。
9、參考文章
1、http://gityuan.com/2016/01/16/thread-pool/
2、https://www.cnblogs.com/dolphin0520/p/3949310.html