Android多執行緒:這是一份全面 & 詳細的執行緒池(ThreadPool)講解教程

Carson_Ho發表於2018-03-01

Android多執行緒:這是一份全面 & 詳細的執行緒池(ThreadPool)講解教程


前言

  • 對於多執行緒,大家應該很熟悉。但是,大家瞭解執行緒池嗎?
  • 今天,我將帶大家全部學習關於執行緒池的所有知識。

目錄

示意圖


1. 簡介

示意圖


2. 工作原理

2.1 核心引數

  • 執行緒池中有6個核心引數,具體如下

示意圖

  • 上述6個引數的配置 決定了 執行緒池的功能,具體設定時機 = 建立 執行緒池類物件時 傳入
  1. ThreadPoolExecutor類 = 執行緒池的真正實現類
  2. 開發者可根據不同需求 配置核心引數,從而實現自定義執行緒池
// 建立執行緒池物件如下
// 通過 構造方法 配置核心引數
   Executor executor = new ThreadPoolExecutor( 
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS, 
                                              sPoolWorkQueue,
                                              sThreadFactory 
                                               );

// 建構函式原始碼分析
    public ThreadPoolExecutor (int corePoolSize,
                               int maximumPoolSize,
                               long keepAliveTime,
                               TimeUnit unit,
                               BlockingQueue<Runnable workQueue>,
                               ThreadFactory threadFactory )
複製程式碼

注:Java 裡已內建4種常用的執行緒池(即 已經配置好核心引數),下面會詳細說明

2.2 內部原理邏輯

當執行緒池執行時,遵循以下工作邏輯

示意圖


3. 使用流程

執行緒池的使用流程如下

// 1. 建立執行緒池
   // 建立時,通過配置執行緒池的引數,從而實現自己所需的執行緒池
   Executor threadPool = new ThreadPoolExecutor(
                                              CORE_POOL_SIZE,
                                              MAXIMUM_POOL_SIZE,
                                              KEEP_ALIVE,
                                              TimeUnit.SECONDS,
                                              sPoolWorkQueue,
                                              sThreadFactory
                                              );
    // 注:在Java中,已內建4種常見執行緒池,下面會詳細說明

// 2. 向執行緒池提交任務:execute()
    // 說明:傳入 Runnable物件
       threadPool.execute(new Runnable() {
            @Override
            public void run() {
                ... // 執行緒執行任務
            }
        });

// 3. 關閉執行緒池shutdown() 
  threadPool.shutdown();
  
  // 關閉執行緒的原理
  // a. 遍歷執行緒池中的所有工作執行緒
  // b. 逐個呼叫執行緒的interrupt()中斷執行緒(注:無法響應中斷的任務可能永遠無法終止)

  // 也可呼叫shutdownNow()關閉執行緒:threadPool.shutdownNow()
  // 二者區別:
  // shutdown:設定 執行緒池的狀態 為 SHUTDOWN,然後中斷所有沒有正在執行任務的執行緒
  // shutdownNow:設定 執行緒池的狀態 為 STOP,然後嘗試停止所有的正在執行或暫停任務的執行緒,並返回等待執行任務的列表
  // 使用建議:一般呼叫shutdown()關閉執行緒池;若任務不一定要執行完,則呼叫shutdownNow()
複製程式碼

4. 常見的4類功能執行緒池

根據引數的不同配置,Java中最常見的執行緒池有4類:

  • 定長執行緒池(FixedThreadPool
  • 定時執行緒池(ScheduledThreadPool )
  • 可快取執行緒池(CachedThreadPool
  • 單執行緒化執行緒池(SingleThreadExecutor

即 對於上述4類執行緒池,Java已根據 應用場景 配置好核心引數

4.1 定長執行緒池(FixedThreadPool)

  • 特點:只有核心執行緒 & 不會被回收、執行緒數量固定、任務佇列無大小限制(超出的執行緒任務會在佇列中等待)
  • 應用場景:控制執行緒最大併發數
  • 具體使用:通過 Executors.newFixedThreadPool() 建立
  • 示例:
// 1. 建立定長執行緒池物件 & 設定執行緒池執行緒數量固定為3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

// 2. 建立好Runnable類執行緒物件 & 需執行的任務
Runnable task =new Runnable(){
  public void run(){
    System.out.println("執行任務啦");
     }
    };
        
// 3. 向執行緒池提交任務:execute()
fixedThreadPool.execute(task);
        
// 4. 關閉執行緒池
fixedThreadPool.shutdown();
複製程式碼

4.2 定時執行緒池(ScheduledThreadPool )

  • 特點:核心執行緒數量固定、非核心執行緒數量無限制(閒置時馬上回收)
  • 應用場景:執行定時 / 週期性 任務
  • 使用:通過*Executors.newScheduledThreadPool()*建立
  • 示例:
// 1. 建立 定時執行緒池物件 & 設定執行緒池執行緒數量固定為5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

// 2. 建立好Runnable類執行緒物件 & 需執行的任務
Runnable task =new Runnable(){
       public void run(){
              System.out.println("執行任務啦");
          }
    };
// 3. 向執行緒池提交任務:schedule()
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延遲1s後執行任務
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms後、每隔1000ms執行任務

// 4. 關閉執行緒池
scheduledThreadPool.shutdown();
複製程式碼

4.3 可快取執行緒池(CachedThreadPool)

  • 特點:只有非核心執行緒、執行緒數量不固定(可無限大)、靈活回收空閒執行緒(具備超時機制,全部回收時幾乎不佔系統資源)、新建執行緒(無執行緒可用時)

任何執行緒任務到來都會立刻執行,不需要等待

  • 應用場景:執行大量、耗時少的執行緒任務
  • 使用:通過*Executors.newCachedThreadPool()*建立
  • 示例:
// 1. 建立可快取執行緒池物件
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

// 2. 建立好Runnable類執行緒物件 & 需執行的任務
Runnable task =new Runnable(){
  public void run(){
        System.out.println("執行任務啦");
            }
    };

// 3. 向執行緒池提交任務:execute()
cachedThreadPool.execute(task);

// 4. 關閉執行緒池
cachedThreadPool.shutdown();

//當執行第二個任務時第一個任務已經完成
//那麼會複用執行第一個任務的執行緒,而不用每次新建執行緒。
複製程式碼

4.4 單執行緒化執行緒池(SingleThreadExecutor)

  • 特點:只有一個核心執行緒(保證所有任務按照指定順序在一個執行緒中執行,不需要處理執行緒同步的問題)

  • 應用場景:不適合併發但可能引起IO阻塞性及影響UI執行緒響應的操作,如資料庫操作,檔案操作等

  • 使用:通過*Executors.newSingleThreadExecutor()*建立

  • 示例:

// 1. 建立單執行緒化執行緒池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

// 2. 建立好Runnable類執行緒物件 & 需執行的任務
Runnable task =new Runnable(){
  public void run(){
        System.out.println("執行任務啦");
            }
    };

// 3. 向執行緒池提交任務:execute()
singleThreadExecutor.execute(task);

// 4. 關閉執行緒池
singleThreadExecutor.shutdown();

複製程式碼

4.5 常見執行緒池 總結 & 對比

示意圖


5. 總結

  • 閱讀本文後,相信你已經非常瞭解執行緒池 & 用法
  • 接下來,我會繼續講解Android開發中關於多執行緒的知識,具體包括Thread類、HandlerHandlerThread等等,有興趣可以繼續關注Carson_Ho的安卓開發筆記

請點贊!因為你的鼓勵是我寫作的最大動力!


歡迎關注carson_ho的微信公眾號

示意圖

示意圖

相關文章