Android 線程池解析與複用原理

作爲面試的常住嘉賓之一,線程池的拷問,估計每個面試官都想問一遍。

下面,我們一起來學習一下。

一、線程池

首先,提到線程池就得說說它的好處,總得來說,可以分爲以下三點:

  1. 複用線程池的線程,避免線程創建和銷燬帶來的性能開銷。
  2. 控制線程池的最大併發數,避免大量線程之間搶佔系統資源而導致的阻塞現象
  3. 能夠對線程進行簡單的管理,並提供定時執行以及制定間隔循環執行等任務

但需要注意的一點,如果只有一個線程,且不需要複用,則不需要用到線程池,沒必要。

我們都知道,Java的線程池共有4中,newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 。但真正的線程池的實現爲 ThreadPoolExecutor ,它提供了一系列參數來配置線程池,上述4中,其實就是不同參數的 ThreadPoolExecutor 而已。

二、ThreadPoolExecutor

接着,我們看看 ThreadPoolExecutor 的構造參數:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

接着看看各個參數是什麼意思:

  • corePoolSize:核心線程數
  • maximumPoolSize:線程池的最大線程數
  • keepAliveTime:非核心線程最大空閒存活時間(如果allowCoreThreadTimeOut = true,這個時間對核心線程也適用)
  • unit:時間單位,有秒,分
  • workQueue:線程池的任務等待隊列
  • threadFactory :線程工程,在創建線程時,可以標準名字,方便調試
  • handler:拒絕策略,當線程池無法處理任務時的拒絕方式

2.1 線程池的中線程的增加策略

那麼,從上面已經知道,線程的增加策略,跟三個參數有關:

  • corPoolSize:核心線程數
  • maximumPoolSize:線程池的最大線程數
    • workQueue:線程池的任務等待隊列

他們之前的關係是這樣的,圖片來源
在這裏插入圖片描述

ThreadPoolExecutor 執行任務時,大致遵循以下規則:

  • 如果線程池中的線程數據未達到核心線程的數量,那麼會直接啓動一個核心線程來執行任務
  • 如果線程池中的線程已經達到了核心線程的數量,那麼任務會被插入到任務隊列中排隊,等待執行
  • 如果隊列已滿,任務無法插入隊列中,如果此時線程數量未達到最大線程數,那麼會立刻啓動一個非核心線程來執行這個任務。
  • 如果隊列已滿,且線程數已經達到最大線程數,則拒絕執行此任務,並調用 RejectedExecutionHandler 的 rejectedExecution 方法來調動調用發。

具體可以看下圖:圖片來源

在這裏插入圖片描述

這裏需要注意的點是,這個隊列需要是有界隊列的;否則線程數量就不會增加到最大線程數,因爲任務一直往隊列增加,而隊列一直未滿。

三、複用原理

上面理解了 ThreadPoolExecutor 的工作原理,那麼線程池的複用又是怎麼回事呢?

首先,當我們使用 ThreadPoolExecutor execute 一個任務時,看看它的源碼:

在這裏插入圖片描述
可以看到,這裏有個 addWorker 的方法,把 runnable 添加進去了,跟蹤進去:
在這裏插入圖片描述
看到,把 runnable 又傳遞給我了 Worker 這個實例,進去再看一下:
在這裏插入圖片描述
原來在 Worker 這裏新建了一個 thread,並任務賦值給 firstTask ,那麼這個 run 什麼時候調用呢,回到 addWorker 方法:
在這裏插入圖片描述
發現,最後執行了 worker 的 start 方法,那麼 worker 的run 方法也就執行了,你可以發現,其實你傳進來的runable 並沒有執行,而是執行 worker 自身的 runnable。
繼續看 run 方法中的 runWorker():
在這裏插入圖片描述
發現它裏面是一個 while 循環,當此時 task 爲空的時候,則使用 getTask() 去拿:
在這裏插入圖片描述
拿到 task 之後,就會執行 task.run ,這樣 runnable 就被複用了。

也可以這樣理解, task 的 thread 複用了任務的 runable ,thread 創建的那幾個,但是 runnable 可以多個,這樣就起到了複用的效果了。

四、線程池分類

上面介紹了 ThreadPoolExecutor 的使用和實現原理,下面介紹一下 Executors 中 4中常見的 線程池

4.1 FixedThreadPool

通過 Executors 的 newFixedThreadPool 創建,它是一種線程數固定的線程池,參數如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

可以看到,核心線程數和最大線程數是一直的,然後空閒時間爲0,隊列則是無線大。
從這裏看,線程一旦創建,就是核心線程,那麼空閒的時候,並不會回收,除非釋放;當任務超過核心線程,則放入到隊列中,等待執行,直到線程空閒出來。

4.2 CachedThreadPool

通過 Executors 的 newCachedThreadPool 創建,它是線程數量不固定的線程池,構造方法如下:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

可以看到,核心線程是0,最大線程數非常大,而時間設置爲60s,也就是說,60s內,如果線程空閒了,就會被回收。注意到的是,如果有任務來了,不會被立即執行,如果需要等待線程創建,並從隊列取出任務。它適合大量的耗時較少的任務,因爲當60s到來時,如果任務還未被執行,則會中斷任務。

4.3 ScheduledThreadPool

它的構造參數爲:

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

看到,它的核心線程數是固定的,最大則是不固定的,適合做一些延時操作。

4.4 SingleThreadExecutor

它的構造參數爲:

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

核心線程數和最大線程數都爲1,確保所有任務都在這個線程中執行。

參考:
https://mp.weixin.qq.com/s/FOs7hiUk7_W2GoZsM1Tc_w

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