標籤:java java線程池 threadpool
一、線程池引入
Java的線程池是Java5.0以後的新功能,它讓開發人員更易開發高效的多線程程式,也讓多線程程式的效能大大提高。Java提供的關於線程池的API是基於原有線程API的,只是用另外一種方式來使用Java的多線程編程功能。
簡單來說,線程池就是一個或者多個線程的集合。一般而言,線程池有一下幾個部分。
- 完成任務的一個或者多個線程
- 用於調度管理的管理線程
- 要求執行的任務隊列
那麼為什麼要使用線程池呢?當短時間內需要處理的任務數量很多時,使用線程池的好處體現在兩點,
- 減少了建立和銷毀線程的次數,每個背景工作執行緒都可以被重複利用,可執行多個任務,就是執行完一個任務,並不被銷毀,而是可以繼續執行其他的任務;
- 可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個線程需要大約1MB記憶體,線程開的越多,消耗的記憶體也就越大,最後死機)。
二、ThreadPoolExecutor
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類。它的繼承關係為主要以下4個ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor,
在ThreadPoolExecutor類中提供了四個構造方法:
public class ThreadPoolExecutor extends AbstractExecutorService { ..... public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); ...}
它的幾個重要的參數的意思,
- corePoolSize:核心池的大小,預設情況下,在建立了線程池後,線程池中的線程數為0,當有任務來之後,就會建立一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩衝隊列當中;
- maximumPoolSize:線程池最大線程數,它表示線上程池中最多能建立多少個線程;
- keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。預設情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程閒置時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,線上程池中的線程數不大於corePoolSize時,keepAliveTime參數對於核心池中的線程也會起作用,直到線程池中的線程數為0;
- unit:參數keepAliveTime的時間單位;
- workQueue:一個阻塞隊列,用來儲存等待執行的任務;
- threadFactory:線程工廠,主要用來建立線程;
- handler:表示當拒絕處理任務時的策略。
舉個簡單的例子:
假如有一個工廠,工廠裡面有10個工人,每個工人同時只能做一件任務。因此只要當10個工人中有工人是閒置,來了任務就分配給閒置工人做;當10個工人都有任務在做時,如果還來了任務,就把任務進行排隊等待;如果說新任務數目增長的速度遠遠大於工人做任務的速度,那麼此時工廠主管可能會想補救措施,比如重新招4個臨時工人進來;然後就將任務也分配給這4個臨時工人做;如果說著14個工人做任務的速度還是不夠,此時工廠主管可能就要考慮不再接收新的任務或者拋棄前面的一些任務了。當這14個工人當中有人空閑時,而新任務增長的速度又比較緩慢,工廠主管可能就考慮辭掉4個臨時工了,只保持原來的10個工人,畢竟請額外的工人是要花錢的。
這個例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。也就是說corePoolSize就是線程池大小,maximumPoolSize在我看來是線程池的一種補救措施,即任務量突然過大時的一種補救措施。
當任務提交給線程池之後的處理策略,這裡總結一下主要有4點:
- 如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會建立一個線程去執行這個任務;
- 如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩衝隊列當中,若添加成功,則該任務會等待空閑線程將其取出去執行;若添加失敗(一般來說是任務緩衝隊列已滿),則會嘗試建立新的線程去執行這個任務;
- 如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
- 如果線程池中的線程數量大於 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許為核心池中的線程設定存活時間,那麼核心池中的線程空閑時間超過keepAliveTime,線程也會被終止。
三、簡單一實例
import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5)); for(int i=0;i<15;i++){ MyTask myTask = new MyTask(i); executor.execute(myTask); System.out.println("線程池中線程數目:"+executor.getPoolSize()+",隊列中等待執行的任務數目:"+ executor.getQueue().size()+",已執行完別的任務數目:"+executor.getCompletedTaskCount()); } executor.shutdown(); }}class MyTask implements Runnable { private int taskNum; public MyTask(int num) { this.taskNum = num; } @Override public void run() { System.out.println("正在執行task "+taskNum); try { Thread.currentThread(); Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task "+taskNum+"執行完畢"); }}
運行結果
正在執行task 0
線程池中線程數目:1,隊列中等待執行的任務數目:0,已執行完別的任務數目:0
線程池中線程數目:2,隊列中等待執行的任務數目:0,已執行完別的任務數目:0
正在執行task 1
線程池中線程數目:3,隊列中等待執行的任務數目:0,已執行完別的任務數目:0
正在執行task 2
線程池中線程數目:4,隊列中等待執行的任務數目:0,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:0,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:1,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:2,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:3,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:4,已執行完別的任務數目:0
線程池中線程數目:5,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
線程池中線程數目:6,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
正在執行task 3
線程池中線程數目:7,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
正在執行task 11
線程池中線程數目:8,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
正在執行task 4
線程池中線程數目:9,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
正在執行task 10
線程池中線程數目:10,隊列中等待執行的任務數目:5,已執行完別的任務數目:0
正在執行task 12
正在執行task 13
正在執行task 14
task 2執行完畢
正在執行task 5
task 1執行完畢
正在執行task 6
task 0執行完畢
正在執行task 7
task 11執行完畢
task 3執行完畢
正在執行task 9
task 14執行完畢
正在執行task 8
task 13執行完畢
task 12執行完畢
task 10執行完畢
task 4執行完畢
task 5執行完畢
task 7執行完畢
task 6執行完畢
task 8執行完畢
task 9執行完畢
從執行結果可以看出,當線程池中線程的數目大於5時,便將任務放入任務緩衝隊列裡面,當任務緩衝隊列滿了之後,便建立新的線程。如果上面程式中,將for迴圈中改成執行20個任務,就會拋出任務拒絕異常了。
四、引用參考
- Java並發編程:線程池的使用 [經典全面]
java內建線程池和隊列詳細講解
Java內建的線程池ThreadPoolExecutor