Java執行緒池(Executor)

凌浩雨發表於2017-08-28
  1. ThreadPoolExecutor使用

/**
* ThreadPoolExecutor 使用測試類
* 在java doc中,並不提倡我們直接使用ThreadPoolExecutor,
* 而是使用Executors類中提供的幾個靜態方法來建立執行緒池
* @author mazaiting
*/
public class ThreadPoolExecutorTest {
   
   /**
    * 當前類主方法入口,在主函式中靜態呼叫即可
    */
   public static void exec() {
       // 例項化執行緒池
       // corePoolSize : 核心池的大小(即執行緒池中的執行緒數目大於這個引數時,提交的任務會被放進任務快取佇列)
       // maximumPoolSize : 執行緒池最大能容忍的執行緒數
       // keepAliveTime : 執行緒存活時間
       // unit : 引數keepAliveTime的時間單位
       // workQueue : 任務快取佇列,用來存放等待執行的任務
       ThreadPoolExecutor executor = new ThreadPoolExecutor(
               5, 10, 200, TimeUnit.MILLISECONDS, 
               new ArrayBlockingQueue<Runnable>(5));
       for (int i = 0; i < 15; i++) {
           MyTask task = new MyTask(i);
           executor.execute(task); // 執行任務
           System.out.println("執行緒池中執行緒數目:"+executor.getPoolSize()+",佇列中等待執行的任務數目:"+
                    executor.getQueue().size()+",已執行玩別的任務數目:"+executor.getCompletedTaskCount());
       }
       // 不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止,但再也不會接受新的任務
       executor.shutdown();
   }
   
   static class MyTask implements Runnable{
       private int taskNum;
       
       public MyTask(int num) {
           this.taskNum = num;
       }
       
       public void run() {
           System.out.println("正在執行task: " + taskNum);
           try {
               // 當前執行緒睡眠4秒
               Thread.currentThread().sleep(4000);
           } catch (InterruptedException e) {}
           System.out.println("task: " + taskNum + "執行完畢");
       }
       
   }
}

執行結果:

正在執行task: 0
執行緒池中執行緒數目:1,佇列中等待執行的任務數目:0,已執行玩別的任務數目:0
執行緒池中執行緒數目:2,佇列中等待執行的任務數目:0,已執行玩別的任務數目:0
正在執行task: 1
正在執行task: 2
執行緒池中執行緒數目:3,佇列中等待執行的任務數目:0,已執行玩別的任務數目:0
執行緒池中執行緒數目:4,佇列中等待執行的任務數目:0,已執行玩別的任務數目:0
正在執行task: 3
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:0,已執行玩別的任務數目:0
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:1,已執行玩別的任務數目:0
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:2,已執行玩別的任務數目:0
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:3,已執行玩別的任務數目:0
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:4,已執行玩別的任務數目:0
執行緒池中執行緒數目:5,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 4
執行緒池中執行緒數目:6,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 10
執行緒池中執行緒數目:7,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 11
執行緒池中執行緒數目:8,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 12
執行緒池中執行緒數目:9,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 13
執行緒池中執行緒數目:10,佇列中等待執行的任務數目:5,已執行玩別的任務數目:0
正在執行task: 14
task: 1執行完畢
task: 0執行完畢
task: 2執行完畢
正在執行task: 5
正在執行task: 6
task: 4執行完畢
正在執行task: 8
task: 3執行完畢
正在執行task: 9
task: 12執行完畢
task: 13執行完畢
正在執行task: 7
task: 10執行完畢
task: 11執行完畢
task: 14執行完畢
task: 5執行完畢
task: 9執行完畢
task: 6執行完畢
task: 8執行完畢
task: 7執行完畢
  1. Executors使用

/**
 * 執行緒池ExecutorService測試類
 * @author mazaiting
 */
public class ExecutorsTest {

    public static void exec() {
        /**
         * CachedThreadPool會建立一個快取區,將初始化的執行緒快取起來。會終止並且從快取中移除已有60秒未被使用的執行緒。
         *  如果執行緒有可用的,就使用之前建立好的執行緒,
         *  如果執行緒沒有可用的,就新建立執行緒。
         *      
         *  重用:快取型池子,先檢視池中有沒有以前建立的執行緒,如果有,就reuse;如果沒有,就建一個新的執行緒加入池中
         *  使用場景:快取型池子通常用於執行一些生存期很短的非同步型任務,因此在一些面向連線的daemon型SERVER中用得不多。
         *  超時:能reuse的執行緒,必須是timeout IDLE內的池中執行緒,預設timeout是60s,超過這個IDLE時長,執行緒例項將被終止及移出池。
         *  結束:注意,放入CachedThreadPool的執行緒不必擔心其結束,超過TIMEOUT不活動,其會自動被終止。
         */
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        
        /**
         * 在FixedThreadPool中,有一個固定大小的池。
         *  如果當前需要執行的任務超過池大小,那麼多出的任務處於等待狀態,直到有空閒下來的執行緒執行任務,
         *  如果當前需要執行的任務小於池大小,空閒的執行緒也不會去銷燬。
         *  
         *  重用:fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的執行緒
         *  固定數目:其獨特之處在於,任意時間點,最多隻能有固定數目的活動執行緒存在,此時如果有新的執行緒要建立,只能放在另外的佇列中等待,直到當前的執行緒中某個執行緒終止直接被移出池子
         *  超時:和cacheThreadPool不同,FixedThreadPool沒有IDLE機制(可能也有,但既然文件沒提,肯定非常長,類似依賴上層的TCP或UDP IDLE機制之類的),
         *  使用場景:所以FixedThreadPool多數針對一些很穩定很固定的正規併發執行緒,多用於伺服器
         *  原始碼分析:從方法的原始碼看,cache池和fixed 池呼叫的是同一個底層池,只不過引數不同:
         *  fixed池執行緒數固定,並且是0秒IDLE(無IDLE)
         *  cache池執行緒數支援0-Integer.MAX_VALUE(顯然完全沒考慮主機的資源承受能力),60秒IDLE
         */
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        
        /**
         * ScheduledThreadPool是一個固定大小的執行緒池,與FixedThreadPool類似,執行的任務是定時執行。
         */
        ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        
        /**
         * SingleThreadExecutor得到的是一個單個的執行緒,這個執行緒會保證你的任務執行完成。
         * 如果當前執行緒意外終止,會建立一個新執行緒繼續執行任務,這和我們直接建立執行緒不同,也和newFixedThreadPool(1)不同。
         */
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        
        /**
         * 建立一個單執行緒執行器,它可以排程命令在給定的延遲之後執行,或者定期執行。(注意,如果此單執行緒由於在關閉前執行失敗而終止,
         * 則新執行緒將在需要執行後續任務時取代其位置。)任務可以保證按順序執行,在任何給定的時間內,不超過一個任務是活動的。
         * 與其他等效的{ @ code newScheduledThreadPool(1)}不同的是,返回的executor不能被重新配置以使用額外的執行緒。
         */
        ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        
        /**
         * 建立一個系統所能容納的執行緒池大小(Runtime.getRuntime().availableProcessors())
         * 作為並行級別
         */
        ExecutorService workStealingPool = Executors.newWorkStealingPool();
        
        run(cachedThreadPool);
//      run(fixedThreadPool);
//      run(scheduledThreadPool);
//      run(singleThreadExecutor);
//      run(singleThreadScheduledExecutor);
//      run(workStealingPool);
    }   
    
    /**
     * 執行任務
     * @param cachedThreadPool 執行緒池服務
     */
    private static void run(ExecutorService threadPool) {
        for (int i = 0; i < 5; i++) {
            final int taskID = i;
            threadPool.execute(new Runnable() {
                
                public void run() {
                    for (int j = 0; j < 5; j++) {
                        try {
                            // 為了測試出效果,讓每次任務執行都需要一定時間   
                            Thread.currentThread().sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("第" + taskID + "次任務的第" + j + "次執行");
                    }
                }
            });
        }
        // 不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止,但再也不會接受新的任務
        threadPool.shutdown();
    }
    
}

run(cachedThreadPool)執行結果如下:

第4次任務的第0次執行
第1次任務的第0次執行
第0次任務的第0次執行
第3次任務的第0次執行
第2次任務的第0次執行
第0次任務的第1次執行
第4次任務的第1次執行
第2次任務的第1次執行
第3次任務的第1次執行
第1次任務的第1次執行
第1次任務的第2次執行
第0次任務的第2次執行
第2次任務的第2次執行
第4次任務的第2次執行
第3次任務的第2次執行
第1次任務的第3次執行
第4次任務的第3次執行
第0次任務的第3次執行
第3次任務的第3次執行
第2次任務的第3次執行
第1次任務的第4次執行
第4次任務的第4次執行
第2次任務的第4次執行
第0次任務的第4次執行
第3次任務的第4次執行

run(fixedThreadPool)執行結果如下:

第2次任務的第0次執行
第1次任務的第0次執行
第0次任務的第0次執行
第2次任務的第1次執行
第1次任務的第1次執行
第0次任務的第1次執行
第1次任務的第2次執行
第0次任務的第2次執行
第2次任務的第2次執行
第1次任務的第3次執行
第0次任務的第3次執行
第2次任務的第3次執行
第1次任務的第4次執行
第0次任務的第4次執行
第2次任務的第4次執行
第3次任務的第0次執行
第4次任務的第0次執行
第3次任務的第1次執行
第4次任務的第1次執行
第3次任務的第2次執行
第4次任務的第2次執行
第4次任務的第3次執行
第3次任務的第3次執行
第3次任務的第4次執行
第4次任務的第4次執行

run(scheduledThreadPool)執行結果如下:

第2次任務的第0次執行
第1次任務的第0次執行
第0次任務的第0次執行
第0次任務的第1次執行
第2次任務的第1次執行
第1次任務的第1次執行
第1次任務的第2次執行
第2次任務的第2次執行
第0次任務的第2次執行
第2次任務的第3次執行
第0次任務的第3次執行
第1次任務的第3次執行
第2次任務的第4次執行
第1次任務的第4次執行
第0次任務的第4次執行
第3次任務的第0次執行
第4次任務的第0次執行
第3次任務的第1次執行
第4次任務的第1次執行
第3次任務的第2次執行
第4次任務的第2次執行
第3次任務的第3次執行
第4次任務的第3次執行
第3次任務的第4次執行
第4次任務的第4次執行

run(singleThreadExecutor)執行結果如下:

第0次任務的第0次執行
第0次任務的第1次執行
第0次任務的第2次執行
第0次任務的第3次執行
第0次任務的第4次執行
第1次任務的第0次執行
第1次任務的第1次執行
第1次任務的第2次執行
第1次任務的第3次執行
第1次任務的第4次執行
第2次任務的第0次執行
第2次任務的第1次執行
第2次任務的第2次執行
第2次任務的第3次執行
第2次任務的第4次執行
第3次任務的第0次執行
第3次任務的第1次執行
第3次任務的第2次執行
第3次任務的第3次執行
第3次任務的第4次執行
第4次任務的第0次執行
第4次任務的第1次執行
第4次任務的第2次執行
第4次任務的第3次執行
第4次任務的第4次執行

run(singleThreadScheduledExecutor)執行結果如下:

第0次任務的第0次執行
第0次任務的第1次執行
第0次任務的第2次執行
第0次任務的第3次執行
第0次任務的第4次執行
第1次任務的第0次執行
第1次任務的第1次執行
第1次任務的第2次執行
第1次任務的第3次執行
第1次任務的第4次執行
第2次任務的第0次執行
第2次任務的第1次執行
第2次任務的第2次執行
第2次任務的第3次執行
第2次任務的第4次執行
第3次任務的第0次執行
第3次任務的第1次執行
第3次任務的第2次執行
第3次任務的第3次執行
第3次任務的第4次執行
第4次任務的第0次執行
第4次任務的第1次執行
第4次任務的第2次執行
第4次任務的第3次執行
第4次任務的第4次執行

run(workStealingPool)未執行出結果,從Java 1.8開始存在


相關文章