java線程池和Executor架構

爲什麼要使用線程池

線程的創建和銷燬會帶來系統的開銷。通過線程池進行線程的管理,可以進行線程的複用,避免線程頻繁的創建和消耗。

《java併發編程的藝術》 合理利用線程池能夠帶來三個好處

  1. 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗。
  2. 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
  3. 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。但是要做到合理的利用線程池,必須對其原理瞭如指掌。

線程池的屬性

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

線程池的大小

線程池的大小有三種。

  1. 當前線程池大小 當前線程池中的線程數量

  2. 核心線程池大小 corePoolSize 線程池提交一個任務。如果當前線程數量<corePoolSize,即使存在空閒線程可以執行任務也會創建一個新的線程。prestartAllCoreThreads方法可以提前創建並啓動所有核心線程。

  3. 最大線程池大小 maxumumPoolSize 線程池允許創建的最大線程數。如果任務隊列滿了,且當前的線程數小於最大線程池大小,則線程池會再創建新的線程執行任務。值得注意的是如果使用了無界的任務隊列這個參數就沒什麼效果。

線程存活時間 keepAliveTime

線程池的工作線程空閒後,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大這個時間,提高線程的利用率。

工作隊列 workQueue

用於保存等待執行的任務的阻塞隊列。 可以選擇以下幾個阻塞隊列。 ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。 LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。 SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。 PriorityBlockingQueue:一個具有優先級的無限阻塞隊列。

線程工廠 threadFactory

用於創建線程的工廠

拒絕策略 handler

工作隊列滿了和線程池大小已經達到最大線程池大小時,說明線程池已經飽和,任務無法處理。這時通過拒絕策列處理任務。 JDK1.5提供的四種策略。 AbortPolicy:直接拋出異常。 CallerRunsPolicy:用調用者所在線程來運行任務。 DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務。 DiscardPolicy:丟棄當前任務。 可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略

線程池執行流程

任務提交流程

  1. 向線程池提交任務
  2. 判斷當前線程池大小是否大於核心線程池大小。如果當前線程池大小大於核心線程池大小則執行步驟3,否則創建線程執行任務。
  3. 判斷工作隊列是否已滿。已滿則繼續執行步驟4,否則將任務加入工作隊列,等待線程池中線程讀取隊列,執行任務。
  4. 判斷當前線程池大小是否大於最大線程池大小。如果當前線程池大小大於最大線程池大小則執行步驟5,否則創建線程執行任務。
  5. 線程池飽和,執行拒絕策列RejectedExecutionHandler.rejectedExecution。

關閉線程池 shutdown和shutdownNow

shutdown:設置線程池狀態爲SHUTDOWN,中斷所有沒有執行任務的線程。此時,則不能再往線程池中添加任何任務,否則將會拋出RejectedExecutionException異常。但是,此時線程池不會立刻退出,直到添加到線程池中的任務都已經處理完成,纔會退出。 shutdownNow:設置線程池狀態爲STOP,嘗試停止所有正在執行任務或暫停任務的線程(通過interrupt)。shutdownNow並不代表線程池就一定立即就能退出,(因爲任務如果沒有處於阻塞等待狀態,interrupt無法中斷)它可能必須要等待所有正在執行的任務都執行完成了才能退出。

Executor框架

Thread即使工作單元也是執行機制。從jdk5起把工作單元和執行機制分開。工作單元是Runnable,Callable,執行機制由Executor框架提供。

Executor框架組成

  1. 任務 實現Runnable和Callable接口
  2. 執行機制 實現Executor接口。主要實現類由ThreadPoolExecutor和ScheduledThreadPoolExecutor。
  3. 異步計算結果 實現Future接口
  4. Executors工具類

ThreadPoolExecutor

ThreadPoolExecutor是線程池的實現。構造函數的參數前面已經介紹。

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

Executors工具類提供創建ThreadPoolExecutor的方法。

CachedThreadPool

CachedThreadPool是一個可緩存的線程池。可以從ThreadPoolExecutor的構造函數參數看出它的功能。核心線程池大小0,最大線程池大小Integer.MAX_VALUE,使用不存儲元素的阻塞隊列SynchronousQueue。所以CachedThreadPool的線程池大小几乎無限大,提交任務時如果沒有空閒的線程(沒線程讀取SynchronousQueue)就會創建一個線程。工作線程閒置60s,將會被終止。

適用:適合於大量執行時間短的任務。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

FixedThreadPool

FixedThreadPool,固定大小的線程池。核心線程池大小和最大線程池大小一致。線程存活時間爲0意味着空閒線程池不會被終止。提交任務時,都會創建一個線程。直到線程數達到指定的線程數nThreads,纔會將任務放入隊列。LinkedBlockingQueue時有界的阻塞隊列,默認大小時Integer.MAX_VALUE。

適用:執行長期的任務,負載比較重的服務器。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

SingleThreadExecutor

SingleThreadExecutor,單線程的線程池。線程池大小隻能是1.

適用:按順序執行任務,且任何時間的不會有多個線程是活動的情況。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

執行任務

線程池執行任務提供了2個方法 execute和submit

execute方法執行任務,沒返回值。

 ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("run");
            }
});

submit方法有返回值Future,通過Future對象可以獲取返回值。

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future<String> submit = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "run";
        }
    });
    //阻塞獲取返回值
    System.out.println(submit.get());

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor和實現了ScheduledExecutorService接口。是用於執行延時任務和定時任務的線程池。功能與Timer類似。Timer是單線程,ScheduledThreadPoolExecutor是多線程。

ScheduledExecutorService API

    /**
     *  延遲執行Runnable任務
     */
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);

    /**
     * 延遲執行Callable任務
     */
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    /**
     * 按固定的頻率period執行任務。initialDelay是第一次運行時的延遲時間。執行時間是 initialDelay+n*period. n是第幾次執行。
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    /**
     * 每次任務執行完後延遲時間delay後再次執行,執行時間是initalDelay+n*(任務執行時間+delay)
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

使用

    //通過Executors創建
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
    //一秒執行一次
    ScheduledFuture<?> scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
        }
    }, 0, 1, TimeUnit.SECONDS);
     Thread.sleep(10000);
     //取消任務
    scheduledFuture.cancel(true);

Future

    /**
    * 取消任務
    * @Param mayInterruptIfRunning 是否中斷執行的任務
    */
    boolean cancel(boolean mayInterruptIfRunning);
    
    /**
    * 是否已經取消
    */
    boolean isCancelled();

    /**
    * 是否已經執行
    */
    boolean isDone();

    /**
     * 阻塞獲取執行結果
     */
    V get() throws InterruptedException, ExecutionException;

    /**
     * 阻塞獲取執行結果,直到超時退出阻塞狀態。
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章