jdk提供的幾種線程池的介紹

Executor框架提供了各種類型的線程池。
ExecutorService 繼承了Executor接口,初步定義了線程池內的方法。然後 ,AbstractExecutorService又實現了ExecutorService裏面定義的方法。

再最後ThreadPoolExecutor 繼承了AbstractExecutorService,裏面是更爲細緻的實現。

jdk定義了一個Executors工廠類,裏面對ThreadPoolExecutor進行封裝。 讓我們用可以Executors取出來newFixedThreadPool(),newWorkStealingPool(),newSingleThreadExecutor(),newCachedThreadPool(),ScheduledExecutorService() 等等不同特點的線程池。

因此 ExecutorService=new ThreadPoolExecutor(…);
就可以創建出自己定製的線程池。
Executors內部也是這樣定製的。
主要實現如下

public class Executors {
    
    /*
    newFixedThreadPool 固定線程線程數量的線程池,該線程池內的線程數量始終不變,  
    如果任務到來,內部有空閒線程,則立即執行,如果沒有或任務數量大於線程數,多出來的任務,  
    則會被暫存到任務隊列中,待線程空閒,按先入先出的順序處理。
    該任務隊列是LinkedBlockingQueue,是無界隊列,如果任務數量特別多,可能會導致內存不足
    */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    
    
    /*
    newSingleThreadExecutor(),該方法返回一個只有一個線程的線程池,多出來的任務會被放到任務隊列內,  
    待線程空閒,按先入先出的順序執行隊列中的任務。隊列也是無界隊列
    */
     public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    
    /*
    newCachedThreadPool() ,該方法返回一個可以根據實際情況調整線程數量的線程池,線程數量不確定,初始是0,最大線程數是Integer.MAX_VALUE,如果有新的空閒線程可以複用,則會優先使用可複用的線程,如果所有線程都在工作,則創建新的線程,所有線程工作結束後,會返回線程池進行復用。但是如果超過60秒空閒,該線程會被銷燬。
    
    該線程池慎用,在無限制的新增線程的場景下,很可能造成系統內存溢出
    */
     public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    
    
    /*
    newSingleThreadScheduledExecutor() 該方法返回一個線程池大小爲1,拓展了在給定時間執行某任務的功能,如在某個固定的延時之後執行,或者週期性執行某任務
    */
      public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
    
    
    /*
    返回同上,但是可以自己定義線程池大小
    */
     public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

}

比較特別是newScheduledThreadPool ,這個是個計劃任務,會在指定的時間,對任務進行調度。

內部有兩個方法。

 public ScheduledFuture<?> scheduleAtFixedRate(...)   
 和
 public ScheduledFuture<?> scheduleWithFixedDelay(...) 

第一個方法設置的定時任務,是從每個任務開始的時間+間隔 執行

第二個方法是 從每個任務結束的時間+間隔 執行


示例:

 ScheduledExecutorService executorService= Executors.newScheduledThreadPool(10);

        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                //打印當前系統秒數
                System.out.println(System.currentTimeMillis()/1000);
            }
        },0,5,TimeUnit.SECONDS);


    }


輸出如下:

1546681976
1546681981
1546681986
1546681991
1546681996

可以看到時間間隔是5秒。
但是請注意!如果任務執行時間 大於調度時間
比如說

 ScheduledExecutorService executorService= Executors.newScheduledThreadPool(10);

        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                //打印當前系統秒數
                try {
                //休眠3秒
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis()/1000);
            }
        },0,2,TimeUnit.SECONDS);


    }

輸出結果:

1546682107
1546682110
1546682113
1546682116
1546682119

這裏任務的時間是3秒,但是調度時間間隔是2秒,結果就會變成定時任務3秒,也就是當任務時間大於間隔的時候,會在任務結束之後 立即調用下一次任務。並不會出現任務堆疊出現的情況。

如果使用的是scheduleWithFixedDelay(…) 方法,則是從任務結束開始調度,那麼時間將變成5秒間隔。

如果自己寫調度器,一定要注意異常的處理,一旦發生異常,所有的調度任務都會終止,所以爲了防止自己的調度器無疾而終,一定要有完善的異常處理機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章