java多線程系列(六)---線程池原理及其使用


線程池

前言:如有不正確的地方,還望指正。

目錄

線程池的優點

  • 重復利用已經創建的線程,減少創建線程和銷毀線程的開銷
  • 提高響應速度,不需要等到線程創建就能立即執行
  • 使用線程池可以進行統一分配,調優和監控
  • 總的來說:降低資源消耗,提高響應速度,提高線程可管理性

線程池原理

  • 提交任務
  • 核心線程池(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/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】,希望能夠持續的為大家帶來好的技術文章!想跟我一起進步么?那就【關注】我吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM