天天看點

【Android 異步操作】線程池 ( 線程池作用 | 線程池種類 | 線程池工作機制 | 線程池任務排程源碼解析 )

文章目錄

  • 一、線程池作用
  • 二、線程池種類
  • 三、線程池工作機制
  • 四、線程池任務排程源碼解析

一、線程池作用

線程池作用 :

① 避免建立線程 : 避免每次使用線程時 , 都需要 建立線程對象 ;

② 統一管理 : 統一管理線程 , 重用存在的線程 , 減少線程對象建立 , 銷毀的開銷 ;

③ 控制并發 : 可 控制線程的最大并發數 , 提高資源使用效率 , 避免資源競争導緻堵塞 ;

二、線程池種類

線程池種類 :

① newCachedThreadPool : 可緩存線程池 , 如果 線程池線程個數已滿 , 回收空閑線程 , 如果沒有空閑線程 , 此時會建立新線程 ;

② newFixedThreadPool : 建立固定大小線程池 , 可設定并發數 , 如果并發數已滿 , 後續任務會 在 等待隊列 中等待可用線程 ;

③ newScheduledThreadPool : 建立固定大小線程池 , 其支援 周期性任務 ;

④ newSingleThreadExecutor : 建立 單線程 線程池 , 該線程池中 隻有一個線程 , 所有的任務按照指定的優先級順序執行 , 如 FIFO 先入先出 ( 先到的先執行 , 後到的後執行 ) , LIFO 後入先出 ( 後到的先執行 ) ;

三、線程池工作機制

線程池線程相關概念:

  • 線程數 : 線程池的 有 最大線程數 MaxSzie , 核心線程數 CoreSize , 非核心線程數就是 MaxSize - CoreSize ;
  • 示例 : 最大線程數 ( MaxSize ) 是 8 個 , 有 3 個核心線程 ( CoreSize ) , 5 個非核心線程 ;
  • 非核心線程 : 閑置超過一定時間 , 就會被回收 ;

線程池任務排程 : 線程池中維護了一個任務隊列 , 線程池啟動後 , 會不停的從任務隊列中取出任務 , 如果有新任務 , 執行如下操作 ;

如果 線程數 小于核心線程數 ( CoreSize ) , 那麼建立核心線程 , 執行上述任務 ;

如果 線程數 大于核心線程數 ( CoreSize ) , 小于最大線程數 ( MaxSize ) , 那麼建立非核心線程 , 執行上述任務 ;

如果 線程數 超過 最大線程數 ( MaxSize )

  • 如果 任務隊列沒滿 , 則将任務放入任務隊列 ;
  • 如果 任務隊列滿了 , 則抛出異常 ; 這裡一般情況下需要手動處理這種情況 , 任務拒絕後 , 處理善後 ;

四、線程池任務排程源碼解析

在 AsyncTask.java 中 , 在靜态代碼塊中 , 自己 自定義建立了線程池 , 沒有使用上述四種線程池 ;

建立線程池時傳入的參數 :

  • CORE_POOL_SIZE : 核心線程數
  • MAXIMUM_POOL_SIZE : 最大線程數
  • KEEP_ALIVE_SECONDS : 閑置時間 , 非核心線程一旦閑置超過一定時間 , 就會被回收
  • TimeUnit.SECONDS : 閑置時間機關 , 秒
  • sPoolWorkQueue : 線程隊列 , 任務隊列
  • sThreadFactory : 線程工廠 , 用于生産線程
public abstract class AsyncTask<Params, Progress, Result> {
    static {
        /**
         * 自定義的線程池 : 
         * CORE_POOL_SIZE : 核心線程數 
         * MAXIMUM_POOL_SIZE : 最大線程數 
         * KEEP_ALIVE_SECONDS : 閑置時間 , 非核心線程一旦閑置超過一定時間 , 就會被回收
         * TimeUnit.SECONDS : 閑置時間機關 , 秒
         * sPoolWorkQueue : 線程隊列 , 任務隊列 
         * sThreadFactory : 線程工廠 , 用于生産線程 
         */  
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    
    private static class SerialExecutor implements Executor {
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
            	// 線程池執行任務 
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
}
           

在 AsyncTask 中 , 調用 ThreadPoolExecutor THREAD_POOL_EXECUTOR 線程池的 void execute(Runnable command) 方法 , 執行線程池任務 ;

在 execute 方法中, 需要執行以下三個步驟 :

public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * 在将來的某個時間執行給定的任務. 
     * 該任務可能在一個新線程中執行, 也可能在目前線程池中已存在的線程中執行.
     * 
     * 如果任務不能被送出執行, 或該線程池失效, 或該線程池線程個數由于超過最大線程數,
     * 任務被 RejectedExecutionHandler 處理. 
     *
     * @param command 向線程池中送出的任務 
     * @throws RejectedExecutionException 如果任務不能被接受, 抛出該異常
     * @throws NullPointerException 如果任務為空, 抛出該異常 
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * 三個步驟:
         *
         * 1. 如果目前運作線程數小于核心線程數 , 嘗試啟動新線程執行該任務, 該任務是線程的第一個任務.
         * 調用 addWorker 方法會檢查運作狀态, 和線程運作個數, 避免在不應該添加線程時執行錯誤操作.
         *
         * 2. 如果任務成功加入隊列, 需要雙重檢查 ( 進入該方法後, 線程池可能關閉 ), 
         * 在進入該方法後, 是否添加了一個線程, 或者線程池是否關閉.
         * 是以, 我們應該再次檢查運作狀态, 如果需要, 将任務放回隊列中, 或者啟動一個新線程.
         *
         * 3. 如果不能将任務入隊, 盡量添加一個新線程. 
         * 如果添加失敗, 此時線程池可能關閉, 或者運作線程數等于最大線程數, 需要拒絕該任務.
         */
        int c = ctl.get();
        // 目前運作的線程數 小于 核心線程數 
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        
  		// 確定處于運作狀态, 然後将任務添加到隊列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 如果不處于運作狀态, 從隊列中移除
            if (! isRunning(recheck) && remove(command))
                reject(command);	// 拒絕任務
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 嘗試添加任務 
        else if (!addWorker(command, false))
            reject(command);
    }
}