1. 程式人生 > >Android 執行緒池

Android 執行緒池

使用執行緒池可以給我們帶來很多好處,首先通過執行緒池中執行緒的重用,減少建立和銷燬執行緒的效能開銷。其次,能控制執行緒池中的併發數,否則會因為大量的執行緒爭奪CPU資源造成阻塞。最後,執行緒池能夠對執行緒進行管理比如使用ScheduledThreadPool來設定延遲N秒後執行任務,並且每隔M秒迴圈執行一次。

下面會通過介紹執行緒池中的真正實現者——ThreadPoolExecutor來引出Android中的4類執行緒池的使用以及特性分析,會加上筆者自己的理解,和自認為比較恰當的比喻,幫助理解。

1.凡事得靠ThreadPoolExecutor(鋪墊環節,懂的直接跳過)

Executor作為一個介面,它的具體實現就是

ThreadPoolExecutor

Android中的執行緒池都是直接或間接通過配置ThreadPoolExecutor來實現不同特性的執行緒池。

先介紹ThreadPoolExecutor的一個常用的構造方法。

  1. /* 
  2. *@ ThreadPoolExecutor構造引數介紹 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */
  6. public ThreadPoolExecutor(  
  7. //核心執行緒數,除非allowCoreThreadTimeOut被設定為true,否則它閒著也不會死
  8. int corePoolSize,   
  9. //最大執行緒數,活動執行緒數量超過它,後續任務就會排隊                   
  10. int maximumPoolSize,   
  11. //超時時長,作用於非核心執行緒(allowCoreThreadTimeOut被設定為true時也會同時作用於核心執行緒),閒置超時便被回收           
  12. long keepAliveTime,                            
  13. //列舉型別,設定keepAliveTime的單位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等
  14. TimeUnit unit,  
  15. //緩衝任務佇列,執行緒池的execute方法會將Runnable物件儲存起來
  16. BlockingQueue<Runnable> workQueue,  
  17. //執行緒工廠介面,只有一個new Thread(Runnable r)方法,可為執行緒池建立新執行緒
  18. ThreadFactory threadFactory)  

ThreadPoolExecutor的各個引數所代表的特性註釋中已經寫的很清楚了,那麼ThreadPoolExecutor執行任務時的心路歷程是什麼樣的呢?(以下用currentSize表示執行緒池中當前執行緒數量)

1)當currentSize<corePoolSize時,沒什麼好說的,直接啟動一個核心執行緒並執行任務。

2)當currentSize>=corePoolSize、並且workQueue未滿時,新增進來的任務會被安排到workQueue中等待執行。

3)當workQueue已滿,但是currentSize<maximumPoolSize時,會立即開啟一個非核心執行緒來執行任務。

4)當currentSize>=corePoolSizeworkQueue已滿、並且currentSize>maximumPoolSize,呼叫handler預設丟擲RejectExecutionExpection異常。

2 Android中的四類執行緒池

Android中最常見的四類具有不同特性的執行緒池分別為FixThreadPoolCachedThreadPoolScheduleThreadPool以及SingleThreadExecutor

2.1     FixThreadPool(一堆人排隊上公廁)

  1. /* 
  2. *@FixThreadPool介紹 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */
  6. publicstatic ExecutorService newFixThreadPool(int nThreads){  
  7.     returnnew ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
  8. }  
  9. //使用
  10. Executors.newFixThreadPool(5).execute(r);  

1)從配置引數來看,FixThreadPool只有核心執行緒,並且數量固定的,也不會被回收,所有執行緒都活動時,因為佇列沒有限制大小,新任務會等待執行。

2【前方高能,筆者腦洞】FixThreadPool其實就像一堆人排隊上公廁一樣,可以無數多人排隊,但是廁所位置就那麼多,而且沒人上時,廁所也不會被拆遷,哈哈o(_)o ,很形象吧。

3)由於執行緒不會回收,FixThreadPool更快地響應外界請求,這也很容易理解,就好像有人突然想上廁所,公廁不是現用現建的。

2.2     SingleThreadPool(公廁裡只有一個坑位)

  1. /* 
  2. *@SingleThreadPool介紹 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */
  6. publicstatic ExecutorService newSingleThreadPool (int nThreads){  
  7.     returnnew FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (110, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );  
  8. }  
  9. //使用
  10. Executors.newSingleThreadPool ().execute(r);  

1)從配置引數可以看出,SingleThreadPool只有一個核心執行緒,確保所有任務都在同一執行緒中按順序完成。因此不需要處理執行緒同步的問題。

2【前方高能,筆者腦洞】可以把SingleThreadPool簡單的理解為FixThreadPool的引數被手動設定為1的情況,即Executors.newFixThreadPool(1).execute(r)。所以SingleThreadPool可以理解為公廁裡只有一個坑位,先來先上。為什麼只有一個坑位呢,因為這個公廁是收費的,收費的大爺上年紀了,只能管理一個坑位,多了就管不過來了(執行緒同步問題)。

2.3     CachedThreadPool(一堆人去一家很大的咖啡館喝咖啡)

  1. /* 
  2. *@CachedThreadPool介紹 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */
  6. publicstatic ExecutorService newCachedThreadPool(int nThreads){  
  7.     returnnew ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());  
  8. }  
  9. //使用
  10. Executors.newCachedThreadPool().execute(r);  

1CachedThreadPool只有非核心執行緒,最大執行緒數非常大,所有執行緒都活動時,會為新任務建立新執行緒,否則利用空閒執行緒(60s空閒時間,過了就會被回收,所以執行緒池中有0個執行緒的可能)處理任務。

2)任務佇列SynchronousQueue相當於一個空集合,導致任何任務都會被立即執行

3【前方高能,筆者腦洞】CachedThreadPool就像是一堆人去一個很大的咖啡館喝咖啡,裡面服務員也很多,隨時去,隨時都可以喝到咖啡。但是為了響應國家的“光碟行動”,一個人喝剩下的咖啡會被保留60秒,供新來的客人使用,哈哈哈哈哈,好惡心啊。如果你運氣好,沒有剩下的咖啡,你會得到一杯新咖啡。但是以前客人剩下的咖啡超過60秒,就變質了,會被服務員回收掉。

4)比較適合執行大量的耗時較少的任務。喝咖啡人挺多的,喝的時間也不長。

2.4    ScheduledThreadPool4個裡面唯一一個有延遲執行和週期重複執行的執行緒池)

  1. /* 
  2. *@ScheduledThreadPool介紹 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */
  6. publicstatic ScheduledExecutorService newScheduledThreadPool(int corePoolSize){  
  7. returnnew ScheduledThreadPoolExecutor(corePoolSize);  
  8. }  
  9. public ScheduledThreadPoolExecutor(int corePoolSize){  
  10. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());  
  11. }  
  12. //使用,延遲1秒執行,每隔2秒執行一次Runnable r
  13. Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 10002000, TimeUnit.MILLISECONDS);  

1)核心執行緒數固定,非核心執行緒(閒著沒活幹會被立即回收)數沒有限制。

2)從上面程式碼也可以看出,ScheduledThreadPool主要用於執行定時任務以及有固定週期的重複任務

----------------------------------------------------------

一,簡述執行緒池:

執行緒池是如何工作的:一系列任務出現後,根據自己的執行緒池安排任務進行。

如圖:

 

執行緒池的好處:

  1. 重用執行緒池中的執行緒,避免因為執行緒的建立和銷燬所帶來的效能開銷。
  2. 能有效控制執行緒池的最大併發數,避免大量的執行緒之間因互相搶佔系統資源而導致的阻塞現象。
  3. 能對執行緒進行簡單的管理。並提供定時執行以及指定間隔迴圈執行等功能。

執行緒池的具體實現為ThreadPoolExeutor,其介面為Executor

ThreadPoolExecutor提供了一系列的引數用於配置執行緒池。

複製程式碼
   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
複製程式碼

引數含義如下:

  1. corePoolSize :執行緒池核心執行緒數,預設情況下,核心執行緒會線上程池中一直存活,即使他們處於閒置狀態。其有一個allowCoreThreadTimeOut屬性如果設定為true,那麼核心執行緒池會有超時策略。超時的時長為第三個引數  keepAliveTime 。如果超時,核心執行緒會被終結。
  2. maxmumPoolSize: 執行緒池所能容忍的最大執行緒數,當活動執行緒數達到這個數值後,後續的新任務會被阻塞。
  3. keepAliveTime:非核心執行緒閒置時的超時時長,超過這個時長就會被非核心執行緒會被回收。這個引數如同第一個引數,如果設定相關屬性後也會作用於核心執行緒。、
  4. unit:指定keepAliveTime的引數時間單位。這是一個列舉,常用的有MILLISECONDS(毫秒)、SECONDS(秒)等
  5. workQueue:執行緒池的任務佇列,通過execute()方法(執行方法)提交的Runable物件會儲存在這個引數中。
  6. threadFactory:執行緒工廠,為執行緒池提供建立新執行緒的功能。

其執行任務時大致遵頊如下規則:

  1. 如果執行緒達到核心執行緒數量,那麼回直接啟動一個核心執行緒。
  2. 執行緒未達到核心執行緒的數量,任務會被插入到任務佇列(workQueue)排隊。
  3. 如果任務佇列已滿導致步驟二無法插入到任務佇列,那麼開啟一個非核心執行緒執行。
  4. 如果步驟三的執行緒數量達到執行緒池規定數目(maxmumPoolSize),那麼拒絕執行此任務。

二,執行緒池的相關類結構解析

看一下執行緒池的類結構圖:

  

  說以下這幾點:

     這是一個抽象工廠模式!

  Executor和Executors的區別:

  Executor僅僅是一個介面,其只有一個方法,execute()方法(執行方法)。

public interface Executor {