面試:講一講Java併發-Executor?

📖摘要


今天分享下 —— 面試:講一講Java併發-Executor? 的一些基本知識,歡迎關注!


🌂分享:Executor框架

在這裏插入圖片描述


😀Executor接口

public interface Executor {
     void execute(Runnable command);
 }

Executor 接口是 Executor 框架中最基礎的部分,定義了一個用於執行Runnableexecute 方法,它沒有實現類只有另一個重要的子接口 ExecutorService


😁ExecutorService接口

//繼承自Executor接口
  public interface ExecutorService extends Executor {
      /**
       * 關閉方法,調用後執行之前提交的任務,不再接受新的任務
       */
      void shutdown();
      /**
       * 從語義上可以看出是立即停止的意思,將暫停所有等待處理的任務並返回這些任務的列表
       */
     List<Runnable> shutdownNow();
     /**
      * 判斷執行器是否已經關閉
      */
     boolean isShutdown();
     /**
      * 關閉後所有任務是否都已完成
      */
     boolean isTerminated();
     /**
      * 中斷
      */
    boolean awaitTermination(long timeout, TimeUnit unit)
         throws InterruptedException;
     /**
      * 提交一個Callable任務
      */
     <T> Future<T> submit(Callable<T> task);
     /**
      * 提交一個Runable任務,result要返回的結果
      */
     <T> Future<T> submit(Runnable task, T result);
     /**
      * 提交一個任務
      */
     Future<?> submit(Runnable task);
     /**
      * 執行所有給定的任務,當所有任務完成,返回保持任務狀態和結果的Future列表
      */
     <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
         throws InterruptedException;
     /**
      * 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。
      */
     <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                   long timeout, TimeUnit unit)
         throws InterruptedException;
     /**
      * 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果。
      */
     <T> T invokeAny(Collection<? extends Callable<T>> tasks)
         throws InterruptedException, ExecutionException;
     /**
      * 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。
      */
     <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                     long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
 }

ExecutorService 接口繼承自 Executor 接口,定義了終止、提交、執行任務、跟蹤任務返回結果等方法。

ExecutorService 的生命週期有三種狀態:運行、關閉和已終止。

  • execute(Runnable command):履行 Ruannable 類型的任務,
  • submit(task):可用來提交 CallableRunnable 任務,並返回代表此任務的 Future 對象
  • shutdown():在完成已提交的任務後封閉辦事,不再接管新任務,
  • shutdownNow():停止所有正在履行的任務並封閉辦事。
  • isTerminated():測試是否所有任務都履行完畢了。,
  • isShutdown():測試是否該 ExecutorService 已被關閉

😂工具類 Executors 的靜態方法

負責生成各種類型的 ExecutorService 線程池實例,共有四種。

  • newFixedThreadPool(numberOfThreads:int):(固定線程池)ExecutorService 創建一個固定線程數量的線程池,並行執行的線程數量不變,線程當前任務完成後,可以被重用執行另一個任務
  • newCachedThreadPool():(可緩存線程池) ExecutorService 創建一個線程池,按需創建新線程,如果線程的當前規模超過了處理需求時,阿麼將回收空閒的線程,而當需求增加時,則可以添加新的線程,線程池的規模不存在任何限制
  • new SingleThreadExecutor();(單線程執行器)線程池中只有一個線程,依次執行任務
  • new ScheduledThreadPool():線程池按時間計劃來執行任務,允許用戶設定執行任務的時間,類似於 timer

🤣Runnable、Callable、Future接口

Runnable接口


// 實現Runnable接口的類將被Thread執行,表示一個基本的任務
  public interface Runnable {
      // run方法就是它所有的內容,就是實際執行的任務
      public abstract void run();
  }

Callable接口


Runnable 接口的區別在於它接收泛型,同時它執行任務後帶有返回內容

// Callable同樣是任務,與Runnable接口的區別在於它接收泛型,同時它執行任務後帶有返回內容
  public interface Callable<V> {
      // 相對於run方法的帶有返回值的call方法
      V call() throws Exception;
}

Runnable 接口和 Callable 接口的實現類,都可以被 ThreadPoolExecutorScheduledThreadPoolExecutor 執行,他們之間的區別是 Runnable 不會返回結果,而 Callable 可以返回結果。

Executors 可以把一個 Runnable 對象轉換成 Callable 對象:

public static Callable<Object> callable(Runnbale task);

當把一個 Callable 對象 (Callable1,Callable2) 提交給 ThreadPoolExecutorScheduledThreadPoolExecutor 執行時,submit(...) 會向我們返回一個 FutureTask 對象。我們執行 FutureTask.get() 來等待任務執行完成,當任務完成後, FutureTask.get() 將返回任務的結果。

Future接口


// Future代表異步任務的執行結果
  public interface Future<V> {
      /**
       * 嘗試取消一個任務,如果這個任務不能被取消(通常是因爲已經執行完了),返回false,否則返回true。
       */
      boolean cancel(boolean mayInterruptIfRunning);
      /**
      * 返回代表的任務是否在完成之前被取消了
      */
     boolean isCancelled();
     /**
      * 如果任務已經完成,返回true
      */
    boolean isDone();
     /**
      * 獲取異步任務的執行結果(如果任務沒執行完將等待)
      */
    V get() throws InterruptedException, ExecutionException;
     /**
      * 獲取異步任務的執行結果(有最常等待時間的限制)
      *
      *  timeout表示等待的時間,unit是它時間單位
      */
     V get(long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
 }

Future 就是對於具體的 Runnable 或者 Callable 任務的執行結果進行取消、查詢是否完成、獲取結果。必要時可以通過 get 方法獲取執行結果,該方法會阻塞直到任務返回結果

  • cancel方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。
  • isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
  • isDone方法表示任務是否已經完成,若任務完成,則返回true;
  • get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
  • get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。

也就是說Future提供了三種功能:

  • 判斷任務是否完成;
  • 能夠中斷任務;
  • 能夠獲取任務執行結果。

線程池的處理流程


在這裏插入圖片描述

線程池優先要創建出基本線程池大小(corePoolSize)的線程數量,沒有達到這個數量時,每次提交新任務都會直接創建一個新線程,當達到了基本線程數量後,又有新任務到達,優先放入等待隊列,如果隊列滿了,纔去創建新的線程(不能超過線程池的最大數maxmumPoolSize)。


😃向線程池提交任務的兩種方式

通過execute()方法


ExecutorService threadpool= Executors.newFixedThreadPool(10);  threadpool.execute(new Runnable(){...});

這種方式提交沒有返回值,也就不能判斷任務是否被線程池執行成功。

通過submit()方法


Future<?> future = threadpool.submit(new Runnable(){...});
    try {
            Object res = future.get();//獲取任務執行結果
        } catch (InterruptedException e) {
            // 處理中斷異常
            e.printStackTrace();
        } catch (ExecutionException e) {
            // 處理無法執行任務異常
            e.printStackTrace();
        }finally{
            // 關閉線程池
            executor.shutdown();
        }

使用 submit 方法來提交任務,它會返回一個 Future 對象,通過 futureget 方法來獲取返回值,get 方法會阻塞住直到任務完成,而使用 get(long timeout, TimeUnit unit) 方法則會阻塞一段時間後立即返回,這時有可能任務沒有執行完。

線程池本身的狀態


volatile int runState;
static final int RUNNING = 0;   //運行狀態
static final int SHUTDOWN = 1;   //關閉狀態
static final int STOP = 2;       //停止
static final int TERMINATED = 3; //終止,終結
  • 當創建線程池後,初始時,線程池處於 RUNNING 狀態;
  • 如果調用了 shutdown() 方法,則線程池處於 SHUTDOWN 狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢,最後終止;
  • 如果調用了 shutdownNow() 方法,則線程池處於 STOP 狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務,返回沒有執行的任務列表;
  • 當線程池處於 SHUTDOWNSTOP 狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲 TERMINATED 狀態。

😄ForkJoinPool

ForkJoinPool 是jdk1.7新引入的線程池,基於 ForkJoin 框架,使用了“分治”的思想。關於ForkJoin框架參考另一篇筆記“Java併發之J.U.C”。

ThreadPoolExecutor 中每個任務都是由單個線程獨立處理的,如果出現一個非常耗時的大任務(比如大數組排序),就可能出現線程池中只有一個線程在處理這個大任務,而其他線程卻空閒着,這會導致CPU負載不均衡:空閒的處理器無法幫助工作繁忙的處理器。

ForkJoinPool 就是用來解決這種問題的:將一個大任務拆分成多個小任務後,使用 fork 可以將小任務分發給其他線程同時處理,使用 join 可以將多個線程處理的結果進行彙總;這實際上就是分治思想的並行版本


😅ForkJoinPool 基本原理

ForkJoinPool 類是 Fork/Join 框架的核心,和 ThreadPoolExecutor 一樣它也是 ExecutorService 接口的實現類。

所以上面的線程池框架圖是比較老的了,新的框架圖如下圖所示:

在這裏插入圖片描述


🎉最後

  • 更多參考精彩博文請看這裏:《陳永佳的博客》

  • 喜歡博主的小夥伴可以加個關注、點個贊哦,持續更新嘿嘿!

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