JDK提供的四種線程池

一、線程池什么時候用,有什么好處?
“線程池”顧名思義,就是存放線程的池子,這個池子可以存放多少線程取決于采用哪種線程池,取決于有多少并發(fā)線程,有多少計算機的硬件資源。使用線程池最直接的好處就是:線程可以重復利用、減少創(chuàng)建和銷毀線程所帶來的系統(tǒng)資源的開銷,提升性能(節(jié)省線程創(chuàng)建的時間開銷,使程序響應(yīng)更快)。
二、JDK自帶4種的線程池(JDK1.5之后)
2.1、固定線程數(shù)的線程池(newFixedThreadPool)
這種線程池里面的線程被設(shè)計成存放固定數(shù)量的線程,具體線程數(shù)可以考慮為CPU核數(shù)*N(N可大可小,取決于并發(fā)的線程數(shù),計算機可用的硬件資源等)。可以通過下面的代碼來獲取當前計算機的CPU的核數(shù)。
int?processors = Runtime.getRuntime().availableProcessors();?FixedThreadPool 是通過 java.util.concurrent.Executors 創(chuàng)建的 ThreadPoolExecutor?實例。這個實例會復用固定數(shù)量的線程處理一個共享的無邊界隊列 。任何時間點,最多有 nThreads 個線程會處于活動狀態(tài)執(zhí)行任務(wù)。如果當所有線程都是活動時,有多的任務(wù)被提交過來,那么它會一致在隊列中等待直到有線程可用。如果任何線程在執(zhí)行過程中因為錯誤而中止,新的線程會替代它的位置來執(zhí)行后續(xù)的任務(wù)。所有線程都會一致存于線程池中,直到顯式的執(zhí)行 ExecutorService.shutdown() 關(guān)閉。
由于阻塞隊列使用了LinkedBlockingQueue,是一個無界隊列,因此永遠不可能拒絕任務(wù)。LinkedBlockingQueue在入隊列和出隊列時使用的是不同的Lock,意味著他們之間不存在互斥關(guān)系,在多CPU情況下,他們能正在在同一時刻既消費,又生產(chǎn),真正做到并行。因此這種線程池不會拒絕任務(wù),而且不會開辟新的線程,也不會因為線程的長時間不使用而銷毀線程。這是典型的生產(chǎn)者----消費者問題,這種線程池適合用在穩(wěn)定且固定的并發(fā)場景,比如服務(wù)器。
下面代碼給出一個固定線程數(shù)的DEMO,每個核綁定了5個線程。
import?java.util.Random;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
public?class?Test?{
????public?static?void?main(String[] args)?{
????????// 獲取計算機有幾個核
????????int?processors = Runtime.getRuntime().availableProcessors();
????????// 第一種線程池:固定個數(shù)的線程池,可以為每個CPU核綁定一定數(shù)量的線程數(shù)
????????ExecutorService fixedThreadPool = Executors.newFixedThreadPool(processors * 5);
????????for?(int?i = 0; i < 10; i++) {
????????????fixedThreadPool.execute(new?Runnable() {
????????????????@Override
????????????????public?void?run()?{
????????????????????System.out.println(Thread.currentThread().getName());
????????????????}
????????????});
????????}
????????fixedThreadPool.shutdown();
????}
}2.2、緩存的線程池(newCachedThreadPool)
?核心池大小為0,線程池最大線程數(shù)目為最大整型,這意味著所有的任務(wù)一提交就會加入到阻塞隊列中。當線程池中的線程60s沒有執(zhí)行任務(wù)就終止,阻塞隊列為SynchronousQueue。
SynchronousQueue的take操作需要put操作等待,put操作需要take操作等待,否則會阻塞(線程池的阻塞隊列不能存儲,所以當目前線程處理忙碌狀態(tài)時,所以開辟新的線程來處理請求),線程進入wait set。
總結(jié)下來:
①這是一個可以無限擴大的線程池;
②適合處理執(zhí)行時間比較小的任務(wù);
③線程空閑時間超過60s就會被殺死,所以長時間處于空閑狀態(tài)的時候,這種線程池幾乎不占用資源;
④阻塞隊列沒有存儲空間,只要請求到來,就必須找到一條空閑線程去處理這個請求,找不到則在線程池新開辟一條線程。
如果主線程提交任務(wù)的速度遠遠大于CachedThreadPool的處理速度,則CachedThreadPool會不斷地創(chuàng)建新線程來執(zhí)行任務(wù),這樣有可能會導致系統(tǒng)耗盡CPU和內(nèi)存資源,所以在使用該線程池是,一定要注意控制并發(fā)的任務(wù)數(shù),否則創(chuàng)建大量的線程可能導致嚴重的性能問題。
import?java.util.Random;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
public?class?Test?{
????public?static?void?main(String[] args)?{
????????// 緩存線程池,無上限
????????ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
????????for?(int?i = 0; i < 100; i++) {
????????????cachedThreadPool.execute(new?Runnable() {
????????????????@Override
????????????????public?void?run()?{
????????????????????System.out.println(Thread.currentThread().getName());
????????????????}
????????????});
????????}
????????cachedThreadPool.shutdown();
????}
}2.3、單個線程的線程池(newSingleThreadExecutor)
SingleThreadExecutor是使用單個worker線程的Executor,作為單一worker線程的線程池,SingleThreadExecutor把corePool和maximumPoolSize均被設(shè)置為1,和FixedThreadPool一樣使用的是無界隊列LinkedBlockingQueue,所以帶來的影響和FixedThreadPool一樣。
對于newSingleThreadExecutor()來說,也是當線程運行時拋出異常的時候會有新的線程加入線程池替他完成接下來的任務(wù)。創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行,所以這個比較適合那些需要按序執(zhí)行任務(wù)的場景。
比如:一些不太重要的收尾,日志等工作可以放到單線程的線程中去執(zhí)行。日志記錄一般情況會比較慢(數(shù)據(jù)量大一般可能不寫入數(shù)據(jù)庫),順序執(zhí)行會拖慢整個接口,堆積更多請求,還可能會對數(shù)據(jù)庫造成影響(事務(wù)在開啟中),所以日志記錄完全可以扔到單線程的線程中去,一條條的處理,也可以認為是一個單消費者的生產(chǎn)者消費者模式。
import?java.util.Random;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
public?class?Test?{
????public?static?void?main(String[] args)?{
????????// 單一線程池,永遠會維護存在一條線程
????????ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
????????for?(int?i = 0; i < 10; i++) {
????????????final?int?j = i;
????????????singleThreadPool.execute(new?Runnable() {
????????????????@Override
????????????????public?void?run()?{
????????????????????System.out.println(Thread.currentThread().getName() + ":"?+ j);
????????????????}
????????????});
????????}
????????singleThreadPool.shutdown();
????????
????}
}?2.4、固定個數(shù)的線程池(newScheduledThreadPool)
相比于第一個固定個數(shù)的線程池強大在? ①可以執(zhí)行延時任務(wù),②也可以執(zhí)行帶有返回值的任務(wù)
import?java.util.concurrent.*;
public?class?Test?{
????public?static?void?main(String[] args)?throws?InterruptedException, ExecutionException{
????????// 第四種線程池:固定個數(shù)的線程池,相比于第一個固定個數(shù)的線程池 強大在 ①可以執(zhí)行延時任務(wù),②也可以執(zhí)行
????????// 有返回值的任務(wù)。
????????// scheduledThreadPool.submit(); 執(zhí)行帶有返回值的任務(wù)
????????// scheduledThreadPool.schedule() 用來執(zhí)行延時任務(wù).
????????// 固定個數(shù)的線程池,可以執(zhí)行延時任務(wù),也可以執(zhí)行帶有返回值的任務(wù)。
????????ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
????????FutureTask ft = new?FutureTask<>(new?Callable() {
????????????@Override
????????????public?String call()?throws?Exception {
????????????????System.out.println("hello");
????????????????return?Thread.currentThread().getName();
????????????}
????????});
????????scheduledThreadPool.submit(ft);
????????
????????// 通過FutureTask對象獲得返回值.
????????String result = ft.get();
????????System.out.println("result : "?+ result);
????????// 執(zhí)行延時任務(wù)
????????scheduledThreadPool.schedule(new?Runnable() {
????????????@Override
????????????public?void?run()?{
????????????????System.out.println(Thread.currentThread().getName() + " : bobm!");
????????????}
????????}, 3L, TimeUnit.SECONDS);
????}
} 作者:FrankYou
出處:cnblogs.com/frankyou/p/9467905.html
