全方位解析-Android中的線程池

筆記文章,沒有廢話,句句關(guān)鍵

線程池的優(yōu)點

  1. 重用線程池里的線程,避免創(chuàng)建和銷毀線程所帶來的性能開銷
  2. 有效控制最大并發(fā)數(shù),避免造成線程間搶占系統(tǒng)資源而造成阻塞
  3. 提高線程可管理性,可以統(tǒng)一進行分配,調(diào)優(yōu)和監(jiān)控的能力

Android中的線程池

復(fù)用Java中的Executor接口,具體實現(xiàn)類為ThreadPoolExecutor,它有以下幾個參數(shù)

參數(shù) 說明 注釋
corePoolSize 線程池中核心線程數(shù)量 一直存活,即使處于閑置狀態(tài)
maximumPoolSize 最大能創(chuàng)建的線程數(shù)量(非核心線程,包含核心線程個數(shù)) 達到這個值后,后續(xù)任務(wù)會阻塞
keepAliveTime 非核心線程最大存活時間 當(dāng)設(shè)置allowCoreThreadTimeOut=true 同樣會做用于核心線程,但通常不會這么做
unit keepAliveTime的時間單位 TimeUnit中的枚舉(時間單位)
workQueue 等待隊列execute 方法提交的Runnable存儲在其中 如果線程池中的線程數(shù)量大于等于corePoolSize的時候,把該任務(wù)放入等待隊列
threadFactory 線程創(chuàng)建工程廠(用來創(chuàng)建線程的) 默認(rèn)使用Executors.defaultThreadFactory() 來創(chuàng)建線程,線程具有相同的NORM_PRIORITY優(yōu)先級并且是非守護線程
handler 線程池的飽和拒絕策略(不常用) 阻塞隊列已且沒有空閑的線程,此時繼續(xù)提交任務(wù),就需要采取一種策略處理該任務(wù),默認(rèn)會拋出異常

常用與關(guān)鍵方法

  • void execute(Runnable run)//提交任務(wù),交由線程池調(diào)度
  • void shutdown()//關(guān)閉線程池,等待任務(wù)執(zhí)行完成
  • void shutdownNow()//關(guān)閉線程池,不等待任務(wù)執(zhí)行完成
  • int getTaskCount()//返回線程池找中所有任務(wù)的數(shù)量 (已完成的任務(wù)+阻塞隊列中的任務(wù))
  • int getCompletedTaskCount()//返回線程池中已執(zhí)行完成的任務(wù)數(shù)量 (已完成的任務(wù))
  • int getPoolSize()//返回線程池中已創(chuàng)建線程數(shù)量
  • int getActiveCount()//返回當(dāng)前正在運行的線程數(shù)量
  • void terminated() 線程池終止時執(zhí)行的策略

線程池的運行狀態(tài)

線程存在運行狀態(tài),如下:

狀態(tài) 說明
NEW 初始狀態(tài),線程被新建,還沒調(diào)用start方法
RUNNABLE 運行狀態(tài),把"運行中"和"就緒"統(tǒng)稱為運行狀態(tài)
BLOCKED 阻塞狀態(tài),表示線程阻塞于鎖
WAITING 等待狀態(tài),需要其他線程通知喚醒
TIME_WAITING 超時等待狀態(tài),表示可以在指定的時間超時后自行返回
TERMINATED 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢
在這里插入圖片描述

根據(jù)線程的運行狀態(tài)思想,我們來展開線程池的運行狀態(tài):

狀態(tài) 說明
RUNNABLE 表示當(dāng)前線程池,存在正在運行的線程
SHUTDOWN 關(guān)閉線程池,不在執(zhí)行新的任務(wù),但會執(zhí)行完線程池正在運行的任務(wù),和添加到隊列中的任務(wù),對應(yīng)shutDown()方法
STOP 立即關(guān)閉線程池,打斷正在運行的任務(wù),且不再處理等待隊列中已添加的任務(wù),對應(yīng)shutDownNow()方法
TIDYING shutDown() / shutDownNow()后進入此狀態(tài),表示隊列和線程池為空,之后進入TERMINATED狀態(tài)
TERMINATED 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢,并調(diào)用 terminated() 通知外界
在這里插入圖片描述

安卓中常用的四種線程池

Executors.newFixedThreadPool()

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  1. 線程數(shù)量固定的線程池
  2. 核心線程與非核心線程數(shù)量相等,意味著只有核心線程。
  3. keepAliveTime = 0L,即使線程池中的線程空閑,也不會被回收。除非調(diào)用shutDown()或shutDownNow()去關(guān)閉線程池。
  4. 可以更快響應(yīng)外界的請求,且任務(wù)阻塞隊列為無邊界隊列(LinkedBlockingQueue()鏈表結(jié)構(gòu)的阻塞隊列),意味著任務(wù)可以無限加入線程池

Executors.newScheduledThreadPool()

   public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              10L, MILLISECONDS,
              new DelayedWorkQueue());
    }
  1. 核心線程數(shù)量固定,非核心線程是無限的,但非核心線程存活時間非常短(10毫秒),閑置后會被回收
  2. 適合執(zhí)行定時任務(wù)和固定周期的重復(fù)任務(wù)
  3. DelayedWorkQueue()優(yōu)先級隊列(堆結(jié)構(gòu)),會根據(jù)定時/延時時間進行排序,延時少,先執(zhí)行

Executors.newSingleThreadExecutor() 常用

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. 核心線程數(shù)為1的線程池。確保所有任務(wù)在同一線程執(zhí)行,因此不用考慮線程同步問題
  2. 阻塞隊列是無界的,可以一直接收任務(wù) 、

Executors.newCachedThreadPool()

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. 沒有核心線程,非核心線程無限,但執(zhí)行完任務(wù)后,線程存活60秒
  2. SynchronousQueue() 是個特殊的隊列,可以理解為無法存儲元素的隊列,因此線程池接收到任務(wù)就會創(chuàng)建線程去執(zhí)行。當(dāng)整個線程都處于空閑狀態(tài),60秒后,線程池中的線程都會因超時而被停止,不占用系統(tǒng)資源。
  3. 根據(jù)以上兩點,此線程池適合執(zhí)行耗時較少但頻繁的任務(wù)執(zhí)行

線程池源碼分析

在分析源碼前,先把線程池的工作流程圖放出來。這樣再去閱讀源碼會更加深刻

在這里插入圖片描述

閱讀源碼的目的:

  • 了解線程池如何復(fù)用
  • 如何巧妙的使用線程池,根據(jù)需求去自定義線程池,以達到暫停/恢復(fù),任務(wù)具有優(yōu)先級,監(jiān)控,分配,調(diào)優(yōu)等
  • 學(xué)習(xí)線程池優(yōu)秀的設(shè)計思想
  • 面試吹牛逼

源碼分析

分析源碼前看下ThreadPoolExecutor類的頭注釋:

public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * The main pool control state, ctl, is an atomic integer packing
     * two conceptual fields
     *   workerCount, indicating the effective number of threads
     *   runState,    indicating whether running, shutting down etc
     *
     * In order to pack them into one int, we limit workerCount to
     * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
     * billion) otherwise representable. If this is ever an issue in
     * the future, the variable can be changed to be an AtomicLong,
     * and the shift/mask constants below adjusted. But until the need
     * arises, this code is a bit faster and simpler using an int.
     *
     * The workerCount is the number of workers that have been
     * permitted to start and not permitted to stop.  The value may be
     * transiently different from the actual number of live threads,
     * for example when a ThreadFactory fails to create a thread when
     * asked, and when exiting threads are still performing
     * bookkeeping before terminating. The user-visible pool size is
     * reported as the current size of the workers set.
     *
     * The runState provides the main lifecycle control, taking on values:
     *
     *   RUNNING:  Accept new tasks and process queued tasks
     *   SHUTDOWN: Don't accept new tasks, but process queued tasks
     *   STOP:     Don't accept new tasks, don't process queued tasks,
     *             and interrupt in-progress tasks
     *   TIDYING:  All tasks have terminated, workerCount is zero,
     *             the thread transitioning to state TIDYING
     *             will run the terminated() hook method
     *   TERMINATED: terminated() has completed
     *
     * The numerical order among these values matters, to allow
     * ordered comparisons. The runState monotonically increases over
     * time, but need not hit each state. The transitions are:
     *
     * RUNNING -> SHUTDOWN
     *    On invocation of shutdown(), perhaps implicitly in finalize()
     * (RUNNING or SHUTDOWN) -> STOP
     *    On invocation of shutdownNow()
     * SHUTDOWN -> TIDYING
     *    When both queue and pool are empty
     * STOP -> TIDYING
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed
     *
     * Threads waiting in awaitTermination() will return when the
     * state reaches TERMINATED.
     *
     * Detecting the transition from SHUTDOWN to TIDYING is less
     * straightforward than you'd like because the queue may become
     * empty after non-empty and vice versa during SHUTDOWN state, but
     * we can only terminate if, after seeing that it is empty, we see
     * that workerCount is 0 (which sometimes entails a recheck -- see
     * below).
     */

這里面有幾段比較重要 :

     * The main pool control state, ctl, is an atomic integer packing
     * two conceptual fields
     *   workerCount, indicating the effective number of threads
     *   runState,    indicating whether running, shutting down etc
     *
     * In order to pack them into one int, we limit workerCount to
     * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
     * billion) otherwise representable. If this is ever an issue in
     * the future, the variable can be changed to be an AtomicLong,
     * and the shift/mask constants below adjusted. But until the need
     * arises, this code is a bit faster and simpler using an int.

線程池控制狀態(tài):clt是一個int類型的原子數(shù),它表示兩個概念,workerCount表示有效線程,籠統(tǒng)來講,相當(dāng)于線程池運行的線程個數(shù),runState,表示線程池運行的狀態(tài),如RUNNING SHUTDOWN等等。
將這兩個字段打包成一個int,int為4字節(jié),有32位,后29位表示線程池的數(shù)量,最大可為5個億,如果不夠可以使用AtomicLong代替。當(dāng)然,運行線程池根本達不到這個數(shù)量。

     *   RUNNING:  Accept new tasks and process queued tasks
     *   SHUTDOWN: Don't accept new tasks, but process queued tasks
     *   STOP:     Don't accept new tasks, don't process queued tasks,
     *             and interrupt in-progress tasks
     *   TIDYING:  All tasks have terminated, workerCount is zero,
     *             the thread transitioning to state TIDYING
     *             will run the terminated() hook method
     *   TERMINATED: terminated() has completed

五種狀態(tài)的含義,上面提到過。

     * RUNNING -> SHUTDOWN
     *    On invocation of shutdown(), perhaps implicitly in finalize()
     * (RUNNING or SHUTDOWN) -> STOP
     *    On invocation of shutdownNow()
     * SHUTDOWN -> TIDYING
     *    When both queue and pool are empty
     * STOP -> TIDYING
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed

線程池狀態(tài)間的變化。等于上面我們畫的圖:

在這里插入圖片描述

結(jié)論:閱讀源碼可以適當(dāng)看下類頭說明,尤其是Android源碼的類頭。可以幫我們更好的理解源碼

從線程池入口進入,理解前面提到ctl是什么?

 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
                                
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS; 
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

Integer.SIZE為32,因此 COUNT_BITS = Integer.SIZE - 3 = 29 CAPACITY表示線程池的容量:

(1 << COUNT_BITS) - 1 ---> 100000000000000000000000000000(30位) -1 -->11111111111111111111111111111(29位)

這代表int32位中,前3位表示線程池狀態(tài)后面29位表示容量
計算結(jié)果的值如下:

在這里插入圖片描述

總結(jié)來說,狀態(tài)值自上而下,數(shù)值越來越大

AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 初始狀態(tài)為RUNNING,線程數(shù)為0

ctlOf(int rs, int wc) 用來獲取int中的值,用來調(diào)用下面兩個方法 :

private static int runStateOf(int c) { return c & ~CAPACITY; } 獲取線程池狀態(tài)
private static int workerCountOf(int c) { return c & CAPACITY; } 獲取線程池中線程的有效線程數(shù)量

計算方式如下:

在這里插入圖片描述

理解上面的計算方式后,開始真正進入源碼閱讀

==入口函數(shù):== Executor.execute()

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        int num=workerCountOf(c);
        //第一步
        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);
    }
  // firstTask 待執(zhí)行的任務(wù) core 是否要啟動核心線程
 private boolean addWorker(Runnable firstTask, boolean core) 

注釋翻譯: 線程池的執(zhí)行分3步

 if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
  • 如果小于corePoolSize核心線程數(shù),直接創(chuàng)先新的線程,并將任務(wù)作為該線程的第一個任務(wù)(firstTask),因為是往新線程執(zhí)行任務(wù),肯定是第一個任務(wù)addWorker()方法的返回值,表示是否添加任務(wù)成功,core = true 啟動核心線程 ,并直接return返回,不再向下執(zhí)行
//  if (workerCountOf(c) < corePoolSize)不滿足,也就是運行線程大于等于核心線程,
//  此時可能為核心線程達到最大,或核心與非核心共存
//  那么繼續(xù)判斷,線程池為運行狀態(tài),同時加入隊列是否成功,
  if (isRunning(c) && workQueue.offer(command)) {
            // 成功! 進行二次校驗,因為此時線程池中的線程可能銷毀
            // (比如非核心到達keepAliveTime指定存活時間而銷毀)或某個線程調(diào)用shutDown()方法
            int recheck = ctl.get();
            // 如果不是RUNNING狀態(tài) ,刪除剛才offer進隊列的任務(wù)
            if (!isRunning(recheck) && remove(command))
                // 執(zhí)行拒絕策略 調(diào)用創(chuàng)建時的handle,執(zhí)行 handler.rejectedExecution(command, this); ,默認(rèn)拋出異常
                reject(command);
                // 如果線程池 沒有正在運行的線程,那么這個線程可能調(diào)用了shutDown()方法,要關(guān)閉了,那么就加入個null任務(wù),
                //也就是不執(zhí)行 新任務(wù),去創(chuàng)建非核心線程 core = false 執(zhí)行等待隊列中的任務(wù)
            else if (workerCountOf(recheck) == 0)
                // 添加個null任務(wù)
                addWorker(null, false);
        }//(isRunning(c) && workQueue.offer(command))不滿足,表示等待隊列已滿,添加任務(wù)失敗,那么就創(chuàng)建非核心線程
        //如果創(chuàng)建失敗。執(zhí)行拒絕策略
         else if (!addWorker(command, false))
            reject(command);

boolean addWorker(Runnable firstTask, boolean core) 方法邏輯:

private boolean addWorker(Runnable firstTask, boolean core) {
        retry: //雙層for循環(huán) retry外層循環(huán)終止標(biāo)識
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 如果rs >= SHUTDOWN不再添加新執(zhí)行 后面的判斷條件可以不關(guān)心
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                //獲取線程池中,正在執(zhí)行任務(wù)的線程
                int wc = workerCountOf(c);
                // 大于容量,不可能,不用看, wc >= (core ? corePoolSize : maximumPoolSize 這個判斷是
                // core = true wc >= corePoolSize 核心線程到達上限 還要創(chuàng)建核心線程,不可以 返回false
                // core = false wc >= maximumPoolSize 達到非核心(包含核心)線程上限 還要創(chuàng)建線程 返回false
                if (wc >= CAPACITY || 
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                    //workerCount +1 
                if (compareAndIncrementWorkerCount(c))
                   //返回外層循環(huán) ,循環(huán)結(jié)束
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 創(chuàng)建 Worker包裝我們的任務(wù) firstTask  
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());
                     // rs < SHUTDOWN 表示是RUNNING   狀態(tài) 
                     // (rs == SHUTDOWN && firstTask == null) 表示SHUTDOWN狀態(tài) 且沒有不再執(zhí)行新的任務(wù)
                    //2種情況都會繼續(xù)執(zhí)行
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                            //上面2中情況 都會加入workers集合
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                            // 設(shè)置 workerAdded = true;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //workerAdded = true; 上面設(shè)置過了
                if (workerAdded) {
                // 啟動線程 
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
       
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            //賦值 firstTask 并用getThreadFactory()得到我們創(chuàng)建時傳入的線程工廠調(diào)用newThread(this);創(chuàng)建線程,并將自己作為Runnable傳入
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
}

Worker是個Runnable,上面的w = new Worker(firstTask); 方法其實就是對任務(wù)的包裝 在構(gòu)造方法中賦值 firstTask 并用getThreadFactory()得到我們傳入的線程工廠調(diào)用newThread(this);創(chuàng)建線程,并將自己作為Runnable傳入。當(dāng)addWorker()方法調(diào)用 t.start() 就會執(zhí)行Worker類中的run()方法。

Worker類

 /** Delegates main run loop to outer runWorker. */
public void run() {
            runWorker(this);
        }
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //賦值firstTask給task
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //要執(zhí)行的任務(wù)不為空直接執(zhí)行,如果等于空調(diào)用getTask()去等待隊列取出任務(wù)
            //對應(yīng)了最開始 (workerCountOf(recheck) == 0) addWorker(null, false); 這段判斷條件
            //之前提過 添加空任務(wù)的目的就是不再執(zhí)行新任務(wù),去執(zhí)行等待隊列的任務(wù)。因此getTask()得以執(zhí)行 
            
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //執(zhí)行任務(wù)前調(diào)用此時已經(jīng)在線程中運行 ,因為這是在Worker中的run方法中
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    //執(zhí)行我們傳入的Runnable
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                    //執(zhí)行任務(wù)后調(diào)用
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask()

 private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            //allowCoreThreadTimeOut 表示非核心線程的超時時間keepAliveTime是否作用于核心線程,默認(rèn)是false
            // 所以只看后面的判斷。  wc > corePoolSize;表示大于核心線程 true
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            //wc > corePoolSize;表示大于核心線程 true 那么就是創(chuàng)建非核心線程,非核心線程取等待隊列的任務(wù)有超時時間 
            //keepAliveTime時間內(nèi)阻塞。取不到 繼續(xù)向下執(zhí)行 任務(wù)完成 非核心線程執(zhí)行完銷毀
            // wc <= corePoolSize; 小于或等于核心線程 workQueue.take();一直阻塞,當(dāng)隊列有任務(wù)時立馬執(zhí)行,這也是為什么核心線程一直存在的原因
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

總結(jié):

  • 核心線程一直存在的原因的是workQueue.take();,非核心線程keepAliveTime時間后銷毀的原因是workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
  • 從以下代碼可以看出。線程池中的任務(wù)達到corePoolSize線程數(shù)時,所有任務(wù)都會先添加到隊列,當(dāng)調(diào)用workQueue.offer(command)時,被 workQueue.take();阻塞的核心線程就會拿到任務(wù),然后去執(zhí)行,這很關(guān)鍵
  • if (isRunning(c) && workQueue.offer(command)) 里的條件當(dāng)都都不滿足時,什么都不會做,之后會往隊列中添加元素。當(dāng)核心線程有空間是getTask中take()阻塞的核心線程會立馬取隊列取出個任務(wù)執(zhí)行
  • beforeExecute(wt, task);afterExecute(task, thrown);會在任務(wù)執(zhí)行前后執(zhí)行
  • 核心線程與非核心線程沒有本質(zhì)上的區(qū)別。只是在獲取任務(wù)時,通過此判斷boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;,非核心超時會結(jié)束,而非核心則會take()阻塞住
int c = ctl.get();
        int num=workerCountOf(c);
        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);

通過源碼 得出前面提到的結(jié)論

在這里插入圖片描述

實戰(zhàn)

根據(jù)學(xué)習(xí)到的源碼,封裝自己的線程池工具類,它有一下幾個特點:

/**
 * 1. 支持按任務(wù)的優(yōu)先級去執(zhí)行,
 * 2. 支持線程池暫停.恢復(fù)(批量文件下載,上傳) ,
 * 3. 異步結(jié)果主動回調(diào)主線程
 * todo 線程池能力監(jiān)控,耗時任務(wù)檢測,定時,延遲,
 *
 * 根據(jù)源碼分析 :當(dāng)工作線程小于核心線程是不會進入隊列
 * 當(dāng)核心線程啟動后 每次都會進入隊列
 */
object MyExecutor {
    private var hiExecutor: ThreadPoolExecutor

    //暫停線程表示:hint:這里為啥不用volatile修飾?
    private  var  isPause: Boolean = false

    private val lock: ReentrantLock = ReentrantLock()

    private val condition: Condition = lock.newCondition()

    //主線程回調(diào)能力的Handler
    private val handler: Handler = Handler(Looper.getMainLooper())
    private val seq = AtomicLong()

    /**
     * 初始化構(gòu)建參數(shù)
     */
    init {
        //獲取cpu數(shù)
        val cpuCount = Runtime.getRuntime().availableProcessors()
        //核心線程數(shù)為 cpu+1
        val corePoolSize = cpuCount + 1
        //非核心線程數(shù)
        val maxPoolSize = corePoolSize * 2 + 1
        //非核心線程超時銷毀時間
        val keepAliveTime = 30L
        //超時時間單位 s
        val unit = TimeUnit.SECONDS
        //相當(dāng)于默認(rèn)實現(xiàn) 創(chuàng)建一個線程
        val threadFactory = ThreadFactory() {
            val thread = Thread(it)
            //hi-executor-0
            thread.name = "hi-executor-" + seq.getAndIncrement()
            return@ThreadFactory Thread(it)
        }



        val priorityBlockingQueue: PriorityBlockingQueue<out Runnable> = PriorityBlockingQueue()
        hiExecutor = object : ThreadPoolExecutor(
            corePoolSize,
            maxPoolSize,
            keepAliveTime,
            unit,
            priorityBlockingQueue as BlockingQueue<Runnable>,
            threadFactory
        ) {
            override fun beforeExecute(t: Thread?, r: Runnable?) {
                priorityBlockingQueue.size
                if (isPause) {
                    try {
                        //條件喚醒線程標(biāo)準(zhǔn)寫法
                        lock.lock()
                        condition.await()
                    } finally {
                        lock.unlock()
                    }

                }
            }

            override fun afterExecute(r: Runnable?, t: Throwable?) {
                //監(jiān)控線程池耗時任務(wù),線程創(chuàng)建數(shù)量,正在運行的數(shù)量
                HiLog.e("已執(zhí)行完的任務(wù)的優(yōu)先級是:" + (r as PriorityRunnable).priority.toString() + Thread.currentThread().name)
                priorityBlockingQueue.size
            }
        }
    }

    /**
     * priority越小 優(yōu)先級越高
     * @IntRange 優(yōu)先級范圍
     *  @JvmOverloads 重載方法 生成多種重載方法
     *  無返回結(jié)果 與正常線程池一樣
     */
    @JvmOverloads
    fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, runnable: Runnable) {
        hiExecutor.execute(PriorityRunnable(priority, runnable))
    }


    @JvmOverloads
    fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, callable: Callable<*>) {
        val newFixedThreadPool = Executors.newCachedThreadPool()

        hiExecutor.execute(PriorityRunnable(priority, callable))
    }

    /**
     * 提供任務(wù)執(zhí)行后返回結(jié)果
     * 并回調(diào)主線程的能力
     *
     * <T> 返回結(jié)果類型
     */
    abstract class Callable<T> : Runnable {

        override fun run() {
            handler.post {
                onPrepare()
            }
            //真正在線程中執(zhí)行
            val t = onBackground()

            handler.post {
                onCompleted(t)
            }
        }

        /**
         * UI線程執(zhí)行
         */
        open fun onPrepare() {
            //預(yù)處理 加載動畫
        }

        /**
         * 線程池中執(zhí)行
         */
        abstract fun onBackground(): T

        /**
         * UI線程執(zhí)行
         * 執(zhí)行完成
         */
        abstract fun onCompleted(t: T)
    }

    /**
     * 重寫的意義在于 PriorityBlockingQueue 這個具有排序的堆結(jié)構(gòu) 每個元素要有比較大小的能力
     * priority 線程優(yōu)先級 runnable執(zhí)行任務(wù)的
     */
    class PriorityRunnable(val priority: Int, private val runnable: Runnable) : Runnable,
        Comparable<PriorityRunnable> {
        override fun run() {
            runnable.run()
        }

        override fun compareTo(other: PriorityRunnable): Int {
            //倒序 priority越小 優(yōu)先級越高
            return if (this.priority < other.priority) 1 else if (this.priority > other.priority) -1 else 0
        }

    }

    //可能多個線程調(diào)用此方法
    @Synchronized
    fun pause() {
        isPause = true
        HiLog.e("hiexecutor is paused")

    }

    @Synchronized
    fun resume() {
        isPause = false
        lock.lock()
        try {
            condition.signalAll()
        } finally {
            lock.unlock()
        }
        HiLog.e("hiexecutor is resume")
    }
}

測試代碼:

    private void priorityExecutor() {
        for (int priority = 0; priority < 10; priority++) {
            int finalPriority = priority;
            HiExecutor.INSTANCE.execute(priority, () -> {
                try {
                    Thread.sleep(1000 - finalPriority * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

利用源碼分析后,我們可以起送封裝自己的線程池,它具有以下幾種能力:

  1. 支持按任務(wù)的優(yōu)先級去執(zhí)行,
  2. 支持線程池暫停.恢復(fù)(批量文件下載,上傳) ,
  3. 異步結(jié)果主動回調(diào)主線程
  4. 線程池能力監(jiān)控,耗時任務(wù)檢測,定時,延遲,

但執(zhí)行中有2個問題:

  • 明明是使用優(yōu)先隊列,但為什么第一次的時候,任務(wù)優(yōu)先級卻是無序的,第二次就好了?
  • 使用pause()方法,線程還在執(zhí)行?

對于問題1。

在這里插入圖片描述

首先線程池的調(diào)度是,系統(tǒng)Cpu利用時間碎片隨機去調(diào)度的。我們的設(shè)置的只能盡可能的去滿足,按照優(yōu)先級去執(zhí)行,但不能保證。更重要的是。通過源碼得知。當(dāng)線程池中,核心線程數(shù)未到最大值時(測試?yán)又惺?strong>5),是不會加入到隊列的,因此也就不會排序。當(dāng)?shù)诙螆?zhí)行任務(wù),線程池就會先加入隊列。然后被take()阻塞的隊列取到任務(wù),然后執(zhí)行,此時就有了優(yōu)先級。 解決:可以在應(yīng)用啟動白屏頁啟動線程。異步去初始化第三方庫,或其他不需要優(yōu)先級的任務(wù)。這樣之后的任務(wù)就都具有優(yōu)先級了

對于問題2:

在這里插入圖片描述

通過源碼分析:

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
    
我們重寫的方法
override fun beforeExecute(t: Thread?, r: Runnable?) {
                priorityBlockingQueue.size
                if (isPause) {
                    try {
                        //條件喚醒線程標(biāo)準(zhǔn)寫法
                        lock.lock()
                        condition.await()
                    } finally {
                        lock.unlock()
                    }

                }
            }

beforeExecute(wt, task);中的方法,只能阻塞當(dāng)前線程,和后續(xù)要執(zhí)行的線程,已經(jīng)在線程中開始執(zhí)行的任務(wù)是無法暫停的。 這其實不是問題。只是種誤解

總結(jié)

通過學(xué)習(xí)源碼,我們的收獲:

  • 學(xué)習(xí)了int字段的多重利用,試想下,如果不采用源碼中的方式,我們至少要維護這幾種字段:sunStatusrunningWorkCountmaxWorkCountshutDownWorkCount,還要保證他們的線程安全。這是種值得借鑒的優(yōu)秀思想。ARouter中也用到類似思想(extars)
  • 清楚線程池的狀態(tài)和工作流程,理解線程池如何復(fù)用與非核心線程的銷毀
  • 找到插手線程池的思路,擴展自己的線程池
  • 通過學(xué)習(xí)源碼,還解決了2個看似不合理,但卻在源碼中有答案的問題。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容