前言:
上篇主要介紹了使用執行緒池的好處以及ExecutorService介面,然後學習了通過Executors工廠類生成滿足不同需求的簡單執行緒池,但是有時候我們需要相對複雜的執行緒池的時候就需要我們自己來自定義一個執行緒池,今天來學習一下ThreadPoolExecutor,然後結合使用場景定義一個按照執行緒優先順序來執行的任務的執行緒池。
執行緒管理相關文章地址:
- Android執行緒管理之Thread使用總結
- Android執行緒管理之ExecutorService執行緒池
- Android執行緒管理之ThreadPoolExecutor自定義執行緒池
- Android執行緒管理之AsyncTask非同步任務
- Android執行緒管理之ThreadLocal理解及應用場景
ThreadPoolExecutor
ThreadPoolExecutor執行緒池用於管理執行緒任務佇列、若干個執行緒。
1.)ThreadPoolExecutor建構函式
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler) ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize: 執行緒池維護執行緒的最少數量
maximumPoolSize:執行緒池維護執行緒的最大數量
keepAliveTime: 執行緒池維護執行緒所允許的空閒時間
unit: 執行緒池維護執行緒所允許的空閒時間的單位
workQueue: 執行緒池所使用的緩衝佇列
threadFactory:執行緒池用於建立執行緒
handler: 執行緒池對拒絕任務的處理策略
2.)建立新執行緒
預設使用Executors.defaultThreadFactory(),也可以通過如下方式
/** * 建立執行緒工廠 */ private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable runnable) { return new Thread(runnable, "download#" + mCount.getAndIncrement()); } };
3.)執行緒建立規則
ThreadPoolExecutor物件初始化時,不建立任何執行執行緒,當有新任務進來時,才會建立執行執行緒。構造ThreadPoolExecutor物件時,需要配置該物件的核心執行緒池大小和最大執行緒池大小
1. 當目前執行執行緒的總數小於核心執行緒大小時,所有新加入的任務,都在新執行緒中處理。
2. 當目前執行執行緒的總數大於或等於核心執行緒時,所有新加入的任務,都放入任務快取佇列中。
3. 當目前執行執行緒的總數大於或等於核心執行緒,並且快取佇列已滿,同時此時執行緒總數小於執行緒池的最大大小,那麼建立新執行緒,加入執行緒池中,協助處理新的任務。
4. 當所有執行緒都在執行,執行緒池大小已經達到上限,並且快取佇列已滿時,就rejectHandler拒絕新的任務。
4.)預設的RejectExecutionHandler拒絕執行策略
1. AbortPolicy 直接丟棄新任務,並丟擲RejectedExecutionException通知呼叫者,任務被丟棄
2. CallerRunsPolicy 用呼叫者的執行緒,執行新的任務,如果任務執行是有嚴格次序的,請不要使用此policy
3. DiscardPolicy 靜默丟棄任務,不通知呼叫者,在處理網路報文時,可以使用此任務,靜默丟棄沒有幾乎處理的報文
4. DiscardOldestPolicy 丟棄最舊的任務,處理網路報文時,可以使用此任務,因為報文處理是有時效的,超過時效的,都必須丟棄
我們也可以寫一些自己的RejectedExecutionHandler,例如拒絕時,直接將執行緒加入快取佇列,並阻塞呼叫者,或根據任務的時間戳,丟棄超過限制的任務。
5.)任務佇列BlockingQueue
排隊原則
1. 如果執行的執行緒少於 corePoolSize,則 Executor 始終首選新增新的執行緒,而不進行排隊。
2. 如果執行的執行緒等於或多於 corePoolSize,則 Executor 始終首選將請求加入佇列,而不新增新的執行緒。
3. 如果無法將請求加入佇列,則建立新的執行緒,除非建立此執行緒超出 maximumPoolSize,在這種情況下,任務將被拒絕。
常見幾種BlockingQueue實現
1. ArrayBlockingQueue : 有界的陣列佇列
2. LinkedBlockingQueue : 可支援有界/無界的佇列,使用連結串列實現
3. PriorityBlockingQueue : 優先佇列,可以針對任務排序
4. SynchronousQueue : 佇列長度為1的佇列,和Array有點區別就是:client thread提交到block queue會是一個阻塞過程,直到有一個worker thread連線上來poll task。
6.)執行緒池執行
execute()方法中,呼叫了三個私有方法
addIfUnderCorePoolSize():線上程池大小小於核心執行緒池大小的情況下,擴充套件執行緒池
addIfUnderMaximumPoolSize():線上程池大小小於執行緒池大小上限的情況下,擴充套件執行緒池
ensureQueuedTaskHandled():保證線上程池關閉的情況下,新加入佇列的執行緒也能正確處理
7.)執行緒池關閉
shutdown():不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止,但再也不會接受新的任務
shutdownNow():立即終止執行緒池,並嘗試打斷正在執行的任務,並且清空任務快取佇列,返回尚未執行的任務
ThreadPoolExecutor實現優先順序執行緒池
1.)定義執行緒優先順序列舉
/** * 執行緒優先順序 */ public enum Priority { HIGH, NORMAL, LOW }
2.)定義執行緒任務
/** * 帶有優先順序的Runnable型別 */ /*package*/ class PriorityRunnable implements Runnable { public final Priority priority;//任務優先順序 private final Runnable runnable;//任務真正執行者 /*package*/ long SEQ;//任務唯一標示 public PriorityRunnable(Priority priority, Runnable runnable) { this.priority = priority == null ? Priority.NORMAL : priority; this.runnable = runnable; } @Override public final void run() { this.runnable.run(); } }
3.)定義一個PriorityExecutor繼承ThreadPoolExecutor
public class PriorityExecutor extends ThreadPoolExecutor { private static final int CORE_POOL_SIZE = 5;//核心執行緒池大小 private static final int MAXIMUM_POOL_SIZE = 256;//最大執行緒池佇列大小 private static final int KEEP_ALIVE = 1;//保持存活時間,當執行緒數大於corePoolSize的空閒執行緒能保持的最大時間。 private static final AtomicLong SEQ_SEED = new AtomicLong(0);//主要獲取新增任務 /** * 建立執行緒工廠 */ private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable runnable) { return new Thread(runnable, "download#" + mCount.getAndIncrement()); } }; /** * 執行緒佇列方式 先進先出 */ private static final Comparator<Runnable> FIFO = new Comparator<Runnable>() { @Override public int compare(Runnable lhs, Runnable rhs) { if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) { PriorityRunnable lpr = ((PriorityRunnable) lhs); PriorityRunnable rpr = ((PriorityRunnable) rhs); int result = lpr.priority.ordinal() - rpr.priority.ordinal(); return result == 0 ? (int) (lpr.SEQ - rpr.SEQ) : result; } else { return 0; } } }; /** * 執行緒佇列方式 後進先出 */ private static final Comparator<Runnable> LIFO = new Comparator<Runnable>() { @Override public int compare(Runnable lhs, Runnable rhs) { if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) { PriorityRunnable lpr = ((PriorityRunnable) lhs); PriorityRunnable rpr = ((PriorityRunnable) rhs); int result = lpr.priority.ordinal() - rpr.priority.ordinal(); return result == 0 ? (int) (rpr.SEQ - lpr.SEQ) : result; } else { return 0; } } }; /** * 預設工作執行緒數5 * * @param fifo 優先順序相同時, 等待佇列的是否優先執行先加入的任務. */ public PriorityExecutor(boolean fifo) { this(CORE_POOL_SIZE, fifo); } /** * @param poolSize 工作執行緒數 * @param fifo 優先順序相同時, 等待佇列的是否優先執行先加入的任務. */ public PriorityExecutor(int poolSize, boolean fifo) { this(poolSize, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO : LIFO), sThreadFactory); } public PriorityExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); } /** * 判斷當前執行緒池是否繁忙 * @return */ public boolean isBusy() { return getActiveCount() >= getCorePoolSize(); } /** * 提交任務 * @param runnable */ @Override public void execute(Runnable runnable) { if (runnable instanceof PriorityRunnable) { ((PriorityRunnable) runnable).SEQ = SEQ_SEED.getAndIncrement(); } super.execute(runnable); } }
裡面定義了兩種執行緒佇列模式: FIFO(先進先出) LIFO(後進先出) 優先順序相同的按照提交先後排序
4.)測試程式
ExecutorService executorService = new PriorityExecutor(5, false); for (int i = 0; i < 20; i++) { PriorityRunnable priorityRunnable = new PriorityRunnable(Priority.NORMAL, new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()+"優先順序正常"); } }); if (i % 3 == 1) { priorityRunnable = new PriorityRunnable(Priority.HIGH, new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()+"優先順序高"); } }); } else if (i % 5 == 0) { priorityRunnable = new PriorityRunnable(Priority.LOW, new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()+"優先順序低"); } }); } executorService.execute(priorityRunnable); }
執行結果:不難發現優先順序高基本上優先執行了 最後執行的基本上優先順序比較低