Android多執行緒之執行緒池

xxq2dream發表於2018-10-07

Android 多執行緒系列

Android執行緒池的真正實現是ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
}
  • corePoolSize:表示核心執行緒數。預設情況下核心執行緒會線上程池中一直存活,即使處於閒置狀態。如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設定為true,那核心執行緒也會有超時策略,時間間隔由keepAliveTime指定
  • maximumPoolSize:執行緒池的最大執行緒數。當活動的執行緒數達到這個數值後,後續的新任務就會被阻塞
  • keepAliveTime:執行緒超時策略的超時時間,超過這個時間,非核心執行緒會被回收,核心執行緒要看allowCoreThreadTimeOut屬性
  • unit:keepAliveTime的單位
  • workQueue:執行緒池的任務佇列,如LinkedBlockingQueue,SynchronousQueue等
  • ThreadFactory:執行緒工廠,為執行緒池建立新執行緒。ThreadFactory是一個介面,只有一個newThread方法
執行過程
  • 如果執行緒池中的執行緒數未達到核心執行緒數,就建立核心執行緒執行新的任務
  • 如果執行緒池中的執行緒數已經達到核心執行緒數,就將任務插入到任務佇列中
  • 如果無法將任務插入佇列,說明佇列已經滿了,這個時候如果執行緒數未達到執行緒池的最大執行緒數,就建立非核心執行緒執行任務
  • 如果任務佇列滿了,執行緒數已經達到最大值,就拒絕執行任務。ThreadPoolExecutor會呼叫RejectedExecutionHandler的rejectedExecution方法通知呼叫者

常見的四種執行緒池

FixedThreadPool
  • 建立方法
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
  • 可以看到預設建構函式需要傳入核心執行緒數,且核心執行緒數和執行緒總數相同,也就是隻有核心執行緒,同時沒有超時時間,任務佇列為LinkedBlockingQueue
  • 由於只有核心執行緒,且沒有超時策略,FixedThreadPool中的執行緒會一直存活,除非執行緒池被關閉。因此能快速響應外界的請求
  • 所有的核心執行緒都處於活動狀態時,新的任務都會處於等待狀態
CachedThreadPool
  • 建立方法
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
  • CachedThreadPool只有非核心執行緒,且數量為Integer.MAX_VALUE,超時時間為60秒。
  • CachedThreadPool採用的任務佇列為SynchronousQueue,因此只要有新的任務來就會立刻被執行。
  • 由於只有非核心執行緒,且有超時策略,當沒有任務時,CachedThreadPool實際上一個執行緒也沒有。
  • CachedThreadPool比較適合執行大量耗時較少的任務
SingleThreadExecutor
  • 建立方法
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}
  • SingleThreadExecutor只有一個核心執行緒,且沒有超時策略,任務佇列為LinkedBlockingQueue。
  • SingleThreadExecutor確保所有的任務都在同一個執行緒中按順序執行,這樣這些任務就不需要處理執行緒同步的問題
ScheduledThreadPool
  • 建立方法
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
}
  • ScheduledThreadPool的核心執行緒數是固定的,必須在建立時指定。非核心執行緒數是沒有限制的。當非核心執行緒空閒時會被立即回收。
  • ScheduledThreadPool的任務佇列為DelayedWorkQueue,因此可以用來執行一些定時任務和具有周期重複的任務

總結

  • 以上的四種常用的執行緒池雖然是系統預設提供的,但是我們在使用時還是要仔細選擇。
  • 比如FixedThreadPool和SingleThreadExecutor中的任務佇列為LinkedBlockingQueue,且沒有指定佇列的預設容量,而LinkedBlockingQueue的預設容量為Integer.MAX_VALUE,因此有可能造成佇列任務過多而佔用過多記憶體甚至發生OOM的情況
  • ScheduledThreadPool和CachedThreadPool的問題則是執行緒最大的數量為Integer.MAX_VALUE,可能導致建立大量的執行緒,引發OOM
  • 以上都是需要注意的地方。另外還是推薦用ThreadPoolExecutor根據實際需要手動建立執行緒池

            歡迎關注我的微信公眾號,期待與你一起學習,一起交流,一起成長!

AntDream


相關文章