(3)線程系列 - 一起解讀ThreadUtils源碼

開發第三方庫的時候往往不會用到rxjava,會使用Thread來處理線程。那麼就直接開門見山,讓我們一步一步解讀ThreadUtils源碼。作者閱讀源碼習慣是先從public方法一個一個硬讀下去,再配合使用方法來使用。

  • public static boolean isMainThread()
    這個方法很簡單,調用Android原生代碼Looper來判斷是否是主線程,關於Android代碼Looper在第四章上會有詳解

  • public static Handler getMainHandler()
    代碼是直接返回HANDLER,而這個HANDLER是一個全局靜態主線程的變量 new Handler(Looper.getMainLooper());

  • public static void runOnUiThread(final Runnable runnable)
    在ui主線程上執行事件,會先通過Looper.myLooper()判斷當前線程是否是ui主線程,如果是直接運行事件,如果不是,則通過上面的HANDLER來運行事件

  • public static ExecutorService getFixedPool(@IntRange(from = 1) final int size)
    該方法返回一個線程池,size參數則是線程池中線程數量,從註釋和方法名中可以知道該方法FixedPool是個對線程數做限制的線程池,用於併發壓力的場景下,那麼我們沿着這個方法看看怎麼實現創建線程池的

  • private static ExecutorService getPoolByTypeAndPriority(final int type)
    getFixedPool方法調用了該方法,該方法運行代碼getPoolByTypeAndPriority(type, Thread.NORM_PRIORITY);Thread.NORM_PRIORITY是個優先級,優先級是5,而線程中是1-10,優先級數字越大則是更優先,爲什麼getFixedPool方法的size參數到這個方法變成了type參數了呢?因爲其他type參數都是負數的,而這個傳遞必須是1以上,所以這個設置xx數量的線程方法,已經自動歸納爲另一個類型了。

  • private static ExecutorService getPoolByTypeAndPriority(final int type, final int priority)
    該方法用到了全局靜態Map<Integer, Map<Integer, ExecutorService>> TYPE_PRIORITY_POOLS,是個通過類型存儲線程池的map,完整代碼註釋如下

    /**
     * 根據類型獲取線程池
     * @param type 類型
     * @param priority 優先級
     * @return 線程池
     */
    private static ExecutorService getPoolByTypeAndPriority(final int type, final int priority) {
        // 同步map安全,防止線程不安全創建多個 Map線程池
        synchronized (TYPE_PRIORITY_POOLS) {
            ExecutorService pool;
            // 通過類型獲取 Map線程池
            Map<Integer, ExecutorService> priorityPools = TYPE_PRIORITY_POOLS.get(type);
            if (priorityPools == null) {
                // 如果沒有 Map線程池 則新建一個
                priorityPools = new ConcurrentHashMap<>();
                pool = ThreadPoolExecutor4Util.createPool(type, priority);
                // 加入線程池
                priorityPools.put(priority, pool);
                // 新建後加入 Map線程池
                TYPE_PRIORITY_POOLS.put(type, priorityPools);
            } else {
                // 根據線程池優先級獲取線程池
                pool = priorityPools.get(priority);
                // 如果沒有該線程池,則創建新的線程池
                if (pool == null) {
                    pool = ThreadPoolExecutor4Util.createPool(type, priority);
                    priorityPools.put(priority, pool);
                }
            }
            return pool;
        }
    }

從上面代碼可知Map TYPE_PRIORITY_POOLS裏面包含了Map ExecutorService,
也就是說線程池包含進了兩層map,最外的第一層是key爲線程池類型的,裏面的第二層是key爲優先級的。

  • ThreadPoolExecutor4Util.createPool
    在上面代碼註釋中可知這句核心關鍵代碼,創建線程池,下面詳細階段這段代碼

  • static final class ThreadPoolExecutor4Util extends ThreadPoolExecutor
    該類繼承了ThreadPoolExecutor來創建線程池,分別創建了以下幾種線程,跟rxjava是不是有點像呢

        /**
         * 創建線程池
         * @param type 類型
         * @param priority 優先級
         * @return 線程池
         */
        private static ExecutorService createPool(final int type, final int priority) {
            switch (type) {
                case TYPE_SINGLE:
                    // 創建 核心線程數爲1,線程池最大線程數量爲1,非核心線程空閒存活時長爲0
                    // 只創建一個線程確保 順序執行的場景,並且只有一個線程在執行
                    return new ThreadPoolExecutor4Util(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue4Util(),
                            new UtilsThreadFactory("single", priority)
                    );
                case TYPE_CACHED:
                    // 創建 核心線程數爲0,線程池最大線程數量爲128,非核心線程空閒存活時長爲60秒
                    // 線程數爲128個一般用於處理執行時間比較短的任務
                    return new ThreadPoolExecutor4Util(0, 128,
                            60L, TimeUnit.SECONDS,
                            new LinkedBlockingQueue4Util(true),
                            new UtilsThreadFactory("cached", priority)
                    );
                case TYPE_IO:
                    // 創建 核心線程數爲可計算資源*2+1,線程池最大線程數量爲可計算資源*2+1,非核心線程空閒存活時長爲30秒
                    return new ThreadPoolExecutor4Util(2 * CPU_COUNT + 1, 2 * CPU_COUNT + 1,
                            30, TimeUnit.SECONDS,
                            new LinkedBlockingQueue4Util(),
                            new UtilsThreadFactory("io", priority)
                    );
                case TYPE_CPU:
                    // 創建 核心線程數爲可計算資源+1,線程池最大線程數量爲可計算資源*2+1,非核心線程空閒存活時長爲30秒
                    return new ThreadPoolExecutor4Util(CPU_COUNT + 1, 2 * CPU_COUNT + 1,
                            30, TimeUnit.SECONDS,
                            new LinkedBlockingQueue4Util(true),
                            new UtilsThreadFactory("cpu", priority)
                    );
                default:
                    // 創建 核心線程數、線程池最大數量爲自定義的,空閒存活時長爲0
                    return new ThreadPoolExecutor4Util(type, type,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue4Util(),
                            new UtilsThreadFactory("fixed(" + type + ")", priority)
                    );
            }
        }

除了創建,還重寫了afterExecute方法和execute方法,不過這兩個方法暫時沒什麼意義所以先不管。

在線程系列第二節講到了創建線程池需要的ThreadFactory threadFactory(線程工廠)、BlockingQueue workQueue(任務隊列),該ThreadUtils類自定義了線程工廠和任務隊列,所以我們先了解該兩類

  • private static final class LinkedBlockingQueue4Util extends LinkedBlockingQueue<Runnable>
    我們先了解LinkedBlockingQueue類,LinkedBlockingQueue這個隊列接收到任務的時候,如果當前線程數小於核心線程數,則新建線程(核心線程)處理任務;如果當前線程數等於核心線程數,則進入隊列等待。由於這個隊列沒有最大值限制,即所有超過核心線程數的任務都將被添加到隊列中,這也就導致了 maximumPoolSize 的設定失效,因爲總線程數永遠不會超過 核心線程數。
    該類重寫了offer方法,如果看過線程池的源碼的同學,會發現如下圖:


    是的,執行pool.execute後會執行隊列的offer方法,那爲什麼重寫offer方法呢,通過上面的解釋可以知道,LinkedBlockingQueue的總線程數永遠不會超過核心線程數,通過重寫它,可以自定義TYPE_CACHED類型的線程池的核心線程爲0,總線程數128。

  • static final class UtilsThreadFactory extends AtomicLong implements ThreadFactory
    線程工廠類,該類重寫只是簡單的創建了一個Thread返回

那麼在這裏我們基本知道了創建線程池的流程了,接下來,是我們調用該工具類時調用線程的整個流程是怎樣的,通過斷點調試,我們是最容易瞭解這個過程的,如圖:


  • onClicktestSingle都是app上業務使用的方法
  • public static <T> void executeByCached(final BaseTask<T> baseTask)
    關鍵代碼是直接創建了一個任務BaseTask傳遞到線程池使用,那麼我們看看BaseTask方法,已經針對run方面的代碼進行了全部註釋,更多的註釋可以下載源碼觀看
        @Override
        public void run() {
            // 判斷是否循環計劃內的
            if (isSchedule) {
                // 因爲如果是在循環內的,那麼runner還是之前的
                if (runner == null) {
                    // 判斷當前狀態如果是New,便賦值state=RUNNING,如果不是New,便返回
                    if (!state.compareAndSet(NEW, RUNNING)) {
                        return;
                    }
                    // 獲取當前線程
                    runner = Thread.currentThread();
                    if (mTimeoutListener != null) {
                        Log.w("ThreadUtils", "Scheduled task doesn't support timeout.");
                    }
                } else {
                    // 如果不是RUNNING便直接返回
                    if (state.get() != RUNNING) {
                        return;
                    }
                }
            } else {
                // 判斷當前狀態如果是New,便賦值state=RUNNING,如果不是New,便返回
                if (!state.compareAndSet(NEW, RUNNING)) {
                    return;
                }
                // 獲取當前線程
                runner = Thread.currentThread();
                if (mTimeoutListener != null) {
                    // 實例化 循環或延遲任務的線程池
                    mExecutorService = new ScheduledThreadPoolExecutor(1, (ThreadFactory) Thread::new);
                    // 調用了延遲運行任務
                    mExecutorService.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            if (!isDone() && mTimeoutListener != null) {
                                timeout();
                                mTimeoutListener.onTimeout();
                            }
                        }
                    }, mTimeoutMillis, TimeUnit.MILLISECONDS);
                }
            }
            try {
                // 執行doInBackground方法獲取值
                final T result = doInBackground();
                // 判斷是否循環計劃內的
                if (isSchedule) {
                    // 如果不是RUNNING便直接返回
                    if (state.get() != RUNNING) {
                        return;
                    }
                    getDeliver().execute(() -> onSuccess(result));
                } else {
                    // 判斷當前狀態如果是RUNNING,便賦值state=COMPLETING,如果不是RUNNING,便返回
                    if (!state.compareAndSet(RUNNING, COMPLETING)) {
                        return;
                    }
                    // 執行成功方法,getDeliver()已經封裝了跳轉ui線程
                    getDeliver().execute(() -> {
                        onSuccess(result);
                        onDone();
                    });
                }
            } catch (InterruptedException ignore) {
                // 被中斷了,判斷當前狀態如果是CANCELLED,便賦值state=INTERRUPTED
                state.compareAndSet(CANCELLED, INTERRUPTED);
            } catch (final Throwable throwable) {
                // 如果出現異常了,判斷當前狀態如果是RUNNING,便賦值EXCEPTIONAL
                if (!state.compareAndSet(RUNNING, EXCEPTIONAL)) {
                    return;
                }
                // 執行成功方法,getDeliver()已經封裝了跳轉ui線程
                getDeliver().execute(() -> {
                    onFail(throwable);
                    onDone();
                });
            }
        }
  • cancel
    ThreadUtils的作者沒明說Activity銷燬是否要寫cancel事件

從上面整個流程可以幾個核心方法,我概括出來:

  1. 擁有者跟rxjava類似的doInBackground,onSucces等等方法
  2. 通過源碼得知有延時、循環週期運行線程等方法
  3. 根據使用方式不一樣來決定io,cpu線程數量,緩存時間,是否使用隊列
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章