Concurrent包經常使用方法簡介

1 Executor接口

留給開發者本身實現的接口,通常狀況下不須要再去實現。它只有一個方法 java

void execute(Runnable command)

2 ExecutorService接口

它繼承自Executor接口,但多了以下3個功能 windows

  1. 終止任務。體如今這幾個方法
    1. void shutdown()
      1. 線程池關閉,不接受新任務,即不能再執行submit(task),若是你硬要執行新任務則會拋異常。
      2. 還沒運行完的任務繼續運行完畢.
    2. List<Runnable> shutdownNow()
      1. 試圖中止全部正在執行的活動任務,但不必定成功。
      2. 返回等待執行的任務列表。
  2. 任務提交者與任務的通訊,就是Future對象的使用。Future有點相似 windows開發中的句柄概念,經過它可對任務進行監控並獲取任務信息。有三種使用方式:
    1. <T> Future<T> submit(Callable<T> task)
    2. Future<?> submit(Runnable task) 
    3. <T> Future<T> submit(Runnable task, T result)
    1. 第一種方法提交的任務,當任務完成後,能夠經過Future.get()獲取任務的返回值.
    2. 第三種方法提交的任務,當任務完成後,能夠經過Future.get()獲取提交時傳遞的參數T result. 好比:
      Future<String> future = service.submit(task,"aaaa");
      String s = future.get();// s="aaaa";
    3. 有趣的是第二種方式,由於它的Future.get()得不到任何內容,它返回值是Null,那爲何不直接把ExecutorService的接口設計成以下這種形式呢
      void submit(Runnable task)

      這是由於Future除了get這種獲取任務信息外,還能夠控制任務,體如今 Future的這個方法上 性能優化

      boolean cancel(boolean mayInterruptIfRunning)

      這個方法的JavaDoc解釋的很清楚 性能

      Attempts to cancel execution of this task. This attempt will
      fail if the task has already completed, already been
      cancelled, or could not be cancelled for some other reason. If
      successful, and this task has not started when cancel is
      called, this task should never run. If the task has already
      started, then the mayInterruptIfRunning parameter determines
      whether the thread executing this task should be interrupted
      in an attempt to stop the task.

      注意:若是任務沒執行完,這3種方式的Future.get()都會阻塞。 fetch

  3. 終止任務後能夠阻塞一下子。由ExecutorService的下面這個方法完成
    boolean awaitTermination(long timeout,TimeUnit unit)

    好比 優化

    service.shutdown();
    service.awaitTermination(20, TimeUnit.SECONDS);

    它的意思是,當service.shutdown()後,主線程阻塞,任務執行結束或者阻塞20秒之後,阻塞解除。 ui

3 ScheduledExecutorService接口

它繼承自ExecutorService接口。顧名思義,它主要用來按期執行任務或週期執行任務。它只有4個方法,都比較好理解。下面用最簡單的例子來作個說明 this

scheduler.schedule(callable,10,SECONDS); //10秒後開始執行callable任務
scheduler.schedule(runnable,10,Seconds); //10秒後開始執行runnable任務
scheduler.scheduleAtFixedRate(runnable, 5, 10, SECONDS); //5秒後開始執行Runnable任務,而後每隔10秒執行一遍該任務.
scheduler.scheduleWithFixedDelay(runnable,5,10,SECONDS); //5秒後開始執行Runnable任務,而後任務執行完後再等10秒就執行一遍任務,即,每隔任務執行的時間+10秒再執行一遍任務。

這4個方法都返回ScheduledFuture對象.它繼承自Future接口,用途和Future差很少。 spa

在JDK以前,計劃任務通常由java.until.Timer類來完成。但相比起 ScheduleExecutorService來講,Timer類的功能較爲簡單,好比下例 線程

private final static long fONCE_PER_DAY = 1000*60*60*24;
Timer timer = new Timer();
timer.scheduleAtFixedRate(fetchMail, getTomorrowMorning4am(), fONCE_PER_DAY);
  1. Timer沒有返對象(我總理解爲一種句柄),不方便細粒度控制任務。
  2. Timer的任務類型比較單一,只有TimerTask一種。
  3. 沒有相似scheduleWithFixedDelay的方法。
  4. 間隔時間的表示方法也不友好。
  5. 性能上也不如ScheduledExecutorService(經過線程池等方式進行了性能優化)出色。

4 Executors類

這個類是一個工廠類,用來生成不一樣特色的ExecutorService或 ScheduledExecutorService實例。這裏主要介紹這些不一樣特色的實例不一樣在什麼地方。

  1. 3類不一樣的ExecutorService實例.
    static ExecutorService newSingleThreadExecutor()

    啓動一個線程負責任務順序執行,順序意味着先提交的任務先執行。其原理是:任務會被提交到一個隊列裏,啓動的那個線程會從隊裏裏取任務,而後執行,執行完,再從隊列裏取下一個任務,再執行。若是該線程執行一個任務失敗,並致使線程結束,系統會建立一個新的線程去執行隊裏裏後續的任務,不會由於前面的任務有異常致使後面無辜的任務沒法執行。

    static ExecutorService newCachedThreadPool()

    啓動N個線程處理N個任務。既然是多個線程運行,意味着任務不會順序運行。一個任務完成後,該線程空閒60秒會被結束。新提交的任務會發現空閒線程,並使用它,若是沒有空閒線程可用則建立新線程。其實,這就是一個動態線程池。適合於規模比較小、建立較頻繁的任務。

    static ExecutorService newFixedThreadPool(int nThreads)

    動態線程池不限制線程的數量,在有些狀況下咱們不但願線程數量不可控,則可使用擁有固定線程數目的線程池。運做原理是:任務被提交到一個隊列裏排隊,線程池裏的空閒線程會把隊列裏的任務提出來執行,每一個線程執行完一個任務後,就去隊列裏抓另外一個任務出來執行。若是一個線程因爲失敗而終止,系統會建立另外一個線程執行後續任務。

  2. 帶ThreadFactory參數生成的ExecutorService實例。以上3種實例建立工做線程時都是用的默認的線程工廠類來建立。也能夠指定本身的線程工廠類來建立,以newSingleThreadExecutor(ThreadFactory threadFactory)爲例:
    //你本身的實現
    class YourselfThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            doXXX;
            return thread;
        }
    }
  3. newSingleThreadExecutor 與 newFixedThreadPool(1) 區別.

    JavaDoc上說:Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.


    ((ThreadPoolExecutor)newFixedThreadPool(1)).setCorePoolSize(3);

    即newFixedThreadPool(1)能夠後期修改線程數,不能保證線程只有一個。而newSingleThreadExecutor能夠保證。

  4. static Callable<Object> callable(Runnable task)

    把Runnable任務轉換成Callable任務.例子以下

    public static void test() throws Exception {
        Runnable task = new Runnable() {
                public void run() {
                    log("begin task");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    log("end task");
                }
            };
        Callable c = Executors.callable(task);
        ExecutorService service = Executors.newCachedThreadPool();
        Future f = service.submit(c);
        System.out.println(f.get());//返回null
        log("end");
    }
            
    private static void log(String message) {
        System.out.println(new Date() + ": " + message);
    }
    
    public static void main(String args[]) throws Exception {
        test();
    }
相關文章
相關標籤/搜索