線程池
線程池的優點
- 重復利用已經創建的線程,減少創建線程和銷毀線程的開銷
- 提高響應速度,不需要等到線程創建就能立即執行
- 使用線程池可以進行統一分配,調優和監控
- 總的來說:降低資源消耗,提高響應速度,提高線程可管理性
線程池原理
- 提交任務
- 核心線程池(corePoolSize)是否已經滿,如果未滿的話就創建線程執行任務
- 否則查看隊列(BlockingQueue)是否已滿,未滿的話,將任務存儲在隊列里
- 如果已經滿了,看線程池(maximumPoolSize)是否已滿,如果滿的話按照拒絕處理任務策略(handler)處理無法執行的任務
- 如果未滿,創建線程執行任務
ThreadPoolExecutor構造參數
- corePoolSize:核心池的大小,構建線程池后,并不會創建線程,當前線程數如果小于corePoolSize時,當要執行任務時,創建一個線程。當當前線程數等于corePoolSize,會將任務放入隊列中
- maximumPoolSize:線程池最大數,也就是線程最多能創建的線程
- keepAliveTime:工作線程空閑后,保持存活的時間。默認情況下,如果當前線程數大于corePoolSize,那么一個線程如果沒有任務,當空閑的時間大于keepAliveTime時,會終止該線程,直到線程數不超過corePoolSize
- workQueue:存儲任務的隊列,有幾種種類型隊列ArrayBlockingQueue(有界緩沖區,基于數組的隊列,先進先出,必須指定大小,可以設置是否保持公平,以FIFO順序訪問),LinkedBlockingQueue(基于鏈表的隊列,如果沒有指定大小,默認為Integer.MAX_VALUE),SynchronousQueue(無界線程池,不管多少任務提交進來,直接運行)
- ThreadFactory:線程工廠,用來創建線程,通過線程工廠可以給創建的線程設置名字
- rejectedExecutionHandler:拒絕處理任務的策略AbortPolicy(直接放棄任務,拋出RejectedExecutionException異常),DiscardPolicy(放棄任務,不拋出異常),DiscardOldestPolicy(放棄最舊的未處理請求,然后重試 execute;如果執行程序已關閉,則會丟棄該任務),CallerRunsPolicy(它直接在 execute 方法的調用線程中運行被拒絕的任務;如果執行程序已關閉,則會丟棄該任務)
ThreadPoolExecutor重要方法
- execute:在將來某個時間執行給定任務
- submit:提交一個任務用于執行,并返回一個表示該任務的 Future,和execute不同的是返回的是一個Future,可以在任務執行完畢之后得到任務執行結果
- shutdown:按過去執行已提交任務的順序發起一個有序的關閉,但是不接受新任務。也就是說,中斷沒有正在執行任務的線程,等待任務執行完畢。
- shutdownnow: 嘗試停止所有的活動執行任務、暫停等待任務的處理,并返回等待執行的任務列表。
線程池狀態
volatile int runState;
static final int RUNNING = 0;
static final int SHUTDOWN = 1;
static final int STOP = 2;
static final int TERMINATED = 3;
- RUNNING:創建線程池后,初始狀態為RUNNING
- SHUTDOWN:執行shutdown方法后,線程池處于SHUTDOWN狀態
- STOP:執行shutdownNow方法后,線程池處于STOP狀態
- TERMINATED:當線程池處于SHUTDOWN或STOP狀態,并且所有工作線程已經銷毀,任務緩存隊列已經清空或執行結束后,線程池被設置為TERMINATED狀態。
源碼分析
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
- command是否為空,是的話拋出異常
- 如果當前線程是否小于corePoolSize,執行addIfUnderCorePoolSize方法,創建線程并執行任務
- 如果當前線程線程數大于等于corePoolSize或者執行addIfUnderCorePoolSize返回false,將當前任務放到隊列中
- 如果線程池狀態不是RUNNING狀態或者任務無法加入到隊列,并且線程數大于最大線程數,執行reject方法
- 流程跟上面的線程池原理相同
配置線程池
- cpu密集型和io密集型線程數的選擇,cpu密集型不需要太多的線程,可以充分利用cpu資源,io密集型適當多線程,io阻塞時可以切換至另一線程。
- 優先級不同的的任務可以使用PriorityBlockingQueue來處理
- 建議使用有界隊列,能夠增加系統的穩定性(如果使用無界隊列,當出現問題時候,隊列無限增長,此時可能會占用大量內存,導致系統出現問題)和預警能力(當出現隊列占滿的時候,拋出異常,可以讓開發人員及時發現)
線程池使用
public static void main(String[] args)
{
final Vector<Integer> vector = new Vector<Integer>();
//corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,BlockingQueue
ThreadPoolExecutor tp = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
final Random random = new Random();
System.out.println(tp.getPoolSize());
for (int i = 0; i < 20; i++)
{
tp.execute(new Runnable()
{
public void run()
{
vector.add(random.nextInt());
}
});
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
tp.shutdown();
System.out.println("已完成的任務:"+tp.getCompletedTaskCount());
System.out.println("活動的線程數:"+tp.getActiveCount());
System.out.println("list大小:"+vector.size());
}
- 上面的代碼我們自己構建了一個ThreadPoolExecutor,而Executor給我們提供了幾個靜態方法用于創建線程池,本質上只是設置了給定的參數而已
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- FixedThreadPool,顧名思義,一個固定大小的線程池,隊列使用的無限制大小的鏈表阻塞隊列
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 單線程線程池,同樣隊列使用的無限制大小的鏈表阻塞隊列
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 無界線程池,無論多少任務,直接運行,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程
我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)
作者:jiajun 出處: http://www.cnblogs.com/-new/