【多執行緒】Android多執行緒學習筆記——執行緒池
前言
轉載請聲明,轉自【//www.cnblogs.com/andy-songwei/p/15313312.html】,謝謝!
Java執行緒池採用了享元設計模式,在系統中維持一定數量的執行緒,用於處理非同步或並發需求,在平時處理非同步或並發任務時被廣泛使用。這裡基於JDK1.8和Android28來整理一些關於執行緒池的知識點。本篇主要包含如下內容:
一、合理使用執行緒池的好處
(1)降低資源消耗。 重用執行緒池,可以降低頻繁創建和銷毀執行緒所帶來的消耗。
(2)提高響應速度。當任務到達時,任務可以不需要等到執行緒創建就能立即執行。假設一個伺服器完成一項任務所需時間為:T1 創建執行緒時間,T2 在執行緒中執行任務的時間,T3 銷毀執行緒時間。如果:T1 + T3 遠大於 T2,則可以採用執行緒池,以提高程式的性能。執行緒池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高程式性能的。它把T1,T3分別安排在程式的啟動和結束的時間段或者一些空閑的時間段,這樣在程式處理客戶請求時,不會有T1,T3的開銷了
(3)提高執行緒的可管理性。執行緒是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還可能導致大量的執行緒之間因互相搶佔系統資源而導致阻塞。使用執行緒池可以進行統一分配、調優和監控。
二、Jdk提供的執行緒池框架
Java中提供的執行緒池框架中,主要類的關係及結構如下圖所示:
- Executor是一個介面,它是Executor框架的基礎,它將任務的提交與任務的執行分離開來。
- ExecutorService介面繼承了Executor,在其上做了一些shutdown()、submit()的擴展,可以說是真正的執行緒池介面;
- AbstractExecutorService抽象類實現了ExecutorService介面中的大部分方法;
- ThreadPoolExecutor是執行緒池的核心實現類,用來執行被提交的任務。
- ScheduledExecutorService介面繼承了ExecutorService介面,提供了帶”周期執行”功能ExecutorService;
- ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後運行命令,或者定期執行命令。ScheduledThreadPoolExecutor比Timer更靈活,功能更強大。
三、執行緒池創建中各個參數的含義
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- corePoolSize
執行緒池的核心執行緒數,默認情況下,核心執行緒會在執行緒池中一直存活,即使它們處於閑置狀態。如果將ThreadPoolExecutor中的allowCoreThreadTimeOut屬性設置為true,那麼閑置的核心執行緒在等待新任務到來時會有超時策略,這個時間間隔由keepAliveTime和unit所指定的時長決定,超過這個時長後核心執行緒就會被終止。
在默認情況下,當提交一個任務時,執行緒池會創建一個新執行緒執行任務,直到當前執行緒數等於corePoolSize,而如果執行了ThreadPoolExecutor的prestartAllCoreThreads()方法,執行緒會提前創建並啟動所有核心執行緒;如果當前執行緒數為corePoolSize,繼續提交的任務會被保存到阻塞隊列中,等待被執行。
- maximumPoolSize
執行緒池所能容納的最大執行緒數。當阻塞隊列(即參數workQueue)滿了後,如果繼續提交任務,則創建新的執行緒執行任務,直到該執行緒池中所有活動的執行緒數達到maximumPoolSize。如果執行緒池中的活動執行緒數達到maximumPoolSize,後續還有新任務,就會執行執行緒池的拒絕策略,由另外一個參數RejectedExecutionHandler來確定。
- keepAliveTime
非核心執行緒閑置時的超時時長,超過這個時長,非核心執行緒就會被回收。默認情況下,該參數只在執行緒數大於corePoolSize時才有用,而當ThreadPoolExecutor中的allowCoreThreadTimeOut屬性設置為true時,keepAliveTime同樣會作用於核心執行緒。
- unit
用於指定keepAliveTime參數的時間單位,這是一個枚舉,常用的有TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等。
- workQueue
執行緒池的阻塞隊列,通過執行緒池的execute方法提交的Runnable對象會存儲在這個參數中。當執行緒池中的執行緒數超過它的corePoolSize的時候,執行緒會進入阻塞隊列進行阻塞等待。通過workQueue,執行緒池實現了阻塞功能。常用阻塞隊列有:
1)ArrayBlockingQueue:一個由數組結構組成的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。默認情況下不保證執行緒公平的訪問隊列,所謂公平訪問隊列是指阻塞的執行緒,可以按照阻塞的先後順序訪問隊列,即先阻塞執行緒先訪問隊列。非公平性是對先等待的執行緒是非公平的,當隊列可用時,阻塞的執行緒都可以爭奪訪問隊列的資格,有可能先阻塞的執行緒最後才訪問隊列。初始化時有參數可以設置
2)LinkedBlockingQueue:一個由鏈表結構組成的有界阻塞隊列(常用)。此隊列的默認和最大長度為Integer.MAX_VALUE,它按照先進先出的原則對元素進行排序。
3)PriorityBlockingQueue:一個支援優先順序排序的無界阻塞隊列。
4)DelayQueue:一個使用優先順序隊列實現的無界阻塞隊列,支援延時獲取元素,隊列使用PriorityQueue來實現。隊列中的元素必須實現Delayed介面,在創建元素時可以指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才能從隊列中提取元素。DelayQueue非常有用,可以將DelayQueue運用在快取系統的設計:可以用DelayQueue保存快取元素的有效期,使用一個執行緒循環查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示快取有效期到了。
5)SynchronousQueue:一個不存儲元素的阻塞隊列(常用)。每一個put操作必須等待一個take操作,否則不能繼續添加元素。SynchronousQueue可以看成是一個傳球手,負責把生產者執行緒處理的數據直接傳遞給消費者執行緒。隊列本身並不存儲任何元素,非常適合傳遞性場景。
6)LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
7)LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
以上的阻塞隊列都實現了BlockingQueue介面,也都是執行緒安全的。
有界隊列就是長度有限,滿了以後生產者會阻塞,無界隊列就是裡面能放無數的東西而不會因為隊列長度限制被阻塞,當然空間限制來源於系統資源的限制,如果處理不及時,導致隊列越來越大越來越大,超出一定的限制致使記憶體超限,作業系統或者JVM幫你解決煩惱,直接把你 OOM kill 省事了。無界也會阻塞,為何?因為阻塞不僅僅體現在生產者放入元素時會阻塞,消費者拿取元素時,如果沒有元素,同樣也會阻塞。
一般來說,我們應該盡量使用有界隊列,因為使用無界隊列作為工作隊列會對執行緒池帶來如下影響:
1)當執行緒池中的執行緒數達到corePoolSize後,新任務將在無界隊列中等待,因此執行緒池中的執行緒數不會超過corePoolSize。
2)由於1,使用無界隊列時maximumPoolSize將是一個無效參數。
3)由於1和2,使用無界隊列時keepAliveTime將是一個無效參數。
4)更重要的,使用無界queue可能會耗盡系統資源,有界隊列則有助於防止資源耗盡,同時即使使用有界隊列,也要盡量控制隊列的大小在一個合適的範圍。
- threadFactory
執行緒工廠,為執行緒池提供創建新執行緒的功能。ThreadFactory是一個介面:
public interface ThreadFactory { Thread newThread(Runnable r); }
通過自定義的執行緒工廠可以給每個新建的執行緒設置一個具有識別度的執行緒名,當然還可以更加自由的對執行緒做更多的設置,比如設置所有的執行緒為守護執行緒。Executors靜態工廠里默認的threadFactory,執行緒的命名規則是「pool-數字-thread-數字」。
1 //java.util.concurrent.Executors.java 2 static class DefaultThreadFactory implements ThreadFactory { 3 private static final AtomicInteger poolNumber = new AtomicInteger(1); 4 private final ThreadGroup group; 5 private final AtomicInteger threadNumber = new AtomicInteger(1); 6 private final String namePrefix; 7 DefaultThreadFactory() { 8 SecurityManager s = System.getSecurityManager(); 9 group = (s != null) ? s.getThreadGroup() : 10 Thread.currentThread().getThreadGroup(); 11 namePrefix = "pool-" + 12 poolNumber.getAndIncrement() + 13 "-thread-"; 14 } 15 public Thread newThread(Runnable r) { 16 Thread t = new Thread(group, r, 17 namePrefix + threadNumber.getAndIncrement(), 18 0); 19 if (t.isDaemon()) 20 t.setDaemon(false); 21 if (t.getPriority() != Thread.NORM_PRIORITY) 22 t.setPriority(Thread.NORM_PRIORITY); 23 return t; 24 } 25 }
- handler
執行緒池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作執行緒,如果繼續提交任務,必須採取一種策略處理該任務,執行緒池提供了4種策略:
(1)AbortPolicy:直接拋出RejectedException異常,默認策略;
(2)CallerRunsPolicy:用調用者所在的執行緒來執行任務;
(3)DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;
(4)DiscardPolicy:直接丟棄任務;
當然也可以根據應用場景實現RejectedExecutionHandler介面,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。
四、執行緒池的工作機制
1)如果當前運行的執行緒少於corePoolSize,則創建新執行緒來執行任務(注意,執行這一步驟需要獲取全局鎖)。
2)如果運行的執行緒等於corePoolSize後仍然有任務提交,則將任務加入BlockingQueue。
3)如果無法將任務加入BlockingQueue(隊列已滿),則創建新的執行緒來處理任務,其上限是當前活動執行緒數不超過maximumPoolSize。
4)如果創建新執行緒時,當前運行的執行緒超出maximumPoolSize,任務將被拒絕,並調用RejectedExecutionHandler.rejectedExecution()方法。
五、提交任務
execute()方法用於提交不需要返回值的任務,所以無法判斷任務是否被執行緒池執行成功。
submit()方法用於提交需要返回值的任務。執行緒池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,並且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前執行緒直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前執行緒一段時間後立即返回,這時候有可能任務沒有執行完。
六、關閉執行緒池
可以通過調用執行緒池的shutdown或shutdownNow方法來關閉執行緒池。它們的原理是遍歷執行緒池中的工作執行緒,然後逐個調用執行緒的interrupt方法來中斷執行緒,所以無法響應中斷的任務可能永遠無法終止。但是它們存在一定的區別,shutdownNow首先將執行緒池的狀態設置成STOP,然後嘗試停止所有的正在執行或暫停任務的執行緒,並返回等待執行任務的列表,而shutdown只是將執行緒池的狀態設置成SHUTDOWN狀態,然後中斷所有沒有正在執行任務的執行緒。
只要調用了這兩個關閉方法中的任意一個,isShutdown方法就會返回true。當所有的任務都已關閉後,才表示執行緒池關閉成功,這時調用isTerminaed方法會返回true。至於應該調用哪一種方法來關閉執行緒池,應該由提交到執行緒池的任務特性決定,通常調用shutdown方法來關閉執行緒池,如果任務不一定要執行完,則可以調用shutdownNow方法
七、 合理地配置執行緒池
要想合理地配置執行緒池,就必須首先分析任務特性,可以從以下幾個角度來分析。
(1)任務的性質:CPU密集型任務、IO密集型任務和混合型任務。
(2)任務的優先順序:高、中和低。
(3)任務的執行時間:長、中和短。
(4)任務的依賴性:是否依賴其他系統資源,如資料庫連接。
性質不同的任務可以用不同規模的執行緒池分開處理。
CPU密集型任務應配置儘可能小的執行緒,如配置Ncpu+1(這為什麼有個+1呢?這是因為在現代電腦中引入了虛擬記憶體技術,cpu讀取真實記憶體時可能該數據不在真實記憶體中,需要到虛擬記憶體中去取,這叫記憶體缺頁。而cpu讀取虛擬記憶體的速度遠小於讀取真實記憶體的速度,此時cpu只能等待。+1之後多了一個執行緒,當出現記憶體缺頁時,正在等待的cpu可以去執行這多出來的執行緒,從而提高cpu的使用率)個執行緒的執行緒池。由於IO密集型任務執行緒並不是一直在執行任務,則應配置儘可能多的執行緒,如2*Ncpu。
混合型的任務,如果可以拆分,將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐量將高於串列執行的吞吐量。如果這兩個任務執行時間相差太大,則沒必要進行分解。可以通過Runtime.getRuntime().availableProcessors()方法獲得當前設備的CPU個數。
優先順序不同的任務可以使用優先順序隊列PriorityBlockingQueue來處理。它可以讓優先順序高的任務先執行。
執行時間不同的任務可以交給不同規模的執行緒池來處理,或者可以使用優先順序隊列,讓執行時間短的任務先執行。
建議使用有界隊列。有界隊列能增加系統的穩定性和預警能力,可以根據需要設大一點兒,比如幾千。如果當時我們設置成無界隊列,那麼執行緒池的隊列就會越來越多,有可能會撐滿記憶體,導致整個系統不可用,而不只是後台任務出現問題。
八、JDK中內置的4類執行緒池
在JDK中的Executors類中提供了4類已經配置好的執行緒池:
其源碼為:
1 //java.util.concurrent.Executors.java 2 public class Executors { 3 ...... 4 //提供的默認的ThreadFactory 5 public static ThreadFactory defaultThreadFactory() { 6 return new DefaultThreadFactory();//在前文中有該類的程式碼 7 } 8 ...... 9 public static ExecutorService newCachedThreadPool() { 10 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 11 60L, TimeUnit.SECONDS, 12 new SynchronousQueue<Runnable>()); 13 } 14 ...... 15 public static ExecutorService newFixedThreadPool(int nThreads) { 16 return new ThreadPoolExecutor(nThreads, nThreads, 17 0L, TimeUnit.MILLISECONDS, 18 new LinkedBlockingQueue<Runnable>()); 19 } 20 ...... 21 public static ExecutorService newSingleThreadExecutor() { 22 return new FinalizableDelegatedExecutorService 23 (new ThreadPoolExecutor(1, 1, 24 0L, TimeUnit.MILLISECONDS, 25 new LinkedBlockingQueue<Runnable>())); 26 } 27 .... 28 } 29 30 public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService { 31 .... 32 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 33 return new ScheduledThreadPoolExecutor(corePoolSize); 34 } 35 .... 36 public ScheduledThreadPoolExecutor(int corePoolSize) { 37 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, 38 new DelayedWorkQueue()); 39 } 40 .... 41 } 42 43 public class ThreadPoolExecutor extends AbstractExecutorService { 44 ...... 45 public ThreadPoolExecutor(int corePoolSize, 46 int maximumPoolSize, 47 long keepAliveTime, 48 TimeUnit unit, 49 BlockingQueue<Runnable> workQueue) { 50 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 51 Executors.defaultThreadFactory(), defaultHandler); 52 } 53 ...... 54 }
1、CachedThreadPool執行緒池
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
通過Executors的newCachedThreadPool方法來創建。它是一種執行緒數量不定的執行緒池它只有非核心執行緒,並且其最大執行緒數為IntegerMAX_VALUE。由於IntegerMAX VALUE是一個很大的數,實際上就相當於最大執行緒數可以任意大。當執行緒池中的執行緒都處於活動狀態時,執行緒池會創建新的執行緒來處理新任務,否則就會利用空閑的執行緒來處理新任務。執行緒池中的空閑執行緒都有超時機制,這個超時時長為60秒,超過60秒閑置執行緒就會被回收。和FixedThreadPool不同的是,CachedThreadPool的任務隊列其實相當於一個空集合。這將導致任何任務都會立即被執行,因為在這種場景下SynchronousQueue是無法插入任務的。SynchronousQueue是一個非常特殊的隊列,在很多情況下可以把它簡單理解為一個無法存儲元素的隊列,由於它在實際中較少使用,這裡就不深入探討它了。從 CachedThreadPool的特性來看,這類執行緒池比較適合執行大量的耗時較少的任務。當整個執行緒池都處於閑置狀態時,執行緒池中的執行緒都會超時而被停止,這個時候CachedThreadPool之中實際上是沒有任何執行緒的,它幾乎是不佔用任何系統資源的。
測試示例:
1 public static void testNewCachedThreadPool() throws InterruptedException { 2 ExecutorService executorService = Executors.newCachedThreadPool(); 3 for (int i = 1; i < 10; i++) { 4 Thread.sleep(10); 5 final int finalI = i; 6 executorService.execute(new Runnable() { 7 @Override 8 public void run() { 9 System.out.println(("執行緒名稱:" + Thread.currentThread().getName() + ",執行" + finalI)); 10 } 11 }); 12 } 13 }
輸出結果:
執行緒名稱:pool-1-thread-1,執行1
執行緒名稱:pool-1-thread-1,執行2
執行緒名稱:pool-1-thread-1,執行3
執行緒名稱:pool-1-thread-1,執行4
執行緒名稱:pool-1-thread-1,執行5
執行緒名稱:pool-1-thread-1,執行6
執行緒名稱:pool-1-thread-1,執行7
執行緒名稱:pool-1-thread-1,執行8
執行緒名稱:pool-1-thread-1,執行9
第一次提交任務後,創建了pool-1-thread-1執行緒,執行完任務後有60s的空閑期,執行每一個任務時間非常短,所以只創建了一個執行緒且所有任務均由其執行。
2、FixedThreadPool執行緒池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
通過Executors的newFixedThreadPool方法來創建。它是一種執行緒數量固定的執行緒池,當執行緒處於空閑狀態時,它們並不會被回收,除非執行緒池被關閉了。當所有的執行緒都處於活動狀態時,新任務都會處於等待狀態,直到有執行緒空閑出來。由於FixedThreadPool只有核心執行緒並且這些核心執行緒不會被回收,這意味著它能夠更加快速地響應外界的請求。 newFixedThreadPool方法的實現如上,可以發現FixedThreadPool中只有核心執行緒並且這些核心執行緒沒有超時機制,另外任務隊列也是沒有大小限制的。
測試示例:
1 public static void testNewFixedThreadPool() { 2 ExecutorService executorService = Executors.newFixedThreadPool(3); 3 for (int i = 0; i < 10; i++) { 4 final int finalI = i; 5 executorService.execute(new Runnable() { 6 @Override 7 public void run() { 8 try { 9 Thread.sleep(2000); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 System.out.println(("執行緒名稱:" + Thread.currentThread().getName() + ",執行" + finalI)); 14 } 15 }); 16 } 17 }
輸出結果:
執行緒名稱:pool-1-thread-3,執行2
執行緒名稱:pool-1-thread-1,執行0
執行緒名稱:pool-1-thread-2,執行1
執行緒名稱:pool-1-thread-2,執行5
執行緒名稱:pool-1-thread-1,執行4
執行緒名稱:pool-1-thread-3,執行3
執行緒名稱:pool-1-thread-3,執行8
執行緒名稱:pool-1-thread-2,執行6
執行緒名稱:pool-1-thread-1,執行7
執行緒名稱:pool-1-thread-3,執行9
配置了3個核心執行緒,只有3個執行緒在執行任務。
3、SingleThreadExecutor執行緒池
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
通過Executors的newSingleThreadExecutor方法來創建。這類執行緒池內部只有一個核心執行緒,它確保所有的任務都在同一個執行緒中按順序執行。SingleThreadExecutor的意義在於統一所有的外界任務到一個執行緒中這使得在這些任務之間不需要處理執行緒同步的問題。
測試示例:
1 public static void testNewSingleThreadExecutor() { 2 ExecutorService executorService = Executors.newSingleThreadExecutor(); 3 for (int i = 1; i < 10; i++) { 4 final int finalI = i; 5 executorService.execute(new Runnable() { 6 @Override 7 public void run() { 8 try { 9 Thread.sleep(2000); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 System.out.println(("執行緒名稱:" + Thread.currentThread().getName() + ",執行" + finalI)); 14 } 15 }); 16 } 17 }
輸出結果:
執行緒名稱:pool-1-thread-1,執行1
執行緒名稱:pool-1-thread-1,執行2
執行緒名稱:pool-1-thread-1,執行3
執行緒名稱:pool-1-thread-1,執行4
執行緒名稱:pool-1-thread-1,執行5
執行緒名稱:pool-1-thread-1,執行6
執行緒名稱:pool-1-thread-1,執行7
執行緒名稱:pool-1-thread-1,執行8
執行緒名稱:pool-1-thread-1,執行9
儘管每個任務都執行時間都超過了2s,但始終只有1個執行緒在執行。
4、ScheduledThreadPool執行緒池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } //ScheduledThreadPoolExecutor是ThreadPoolExecutor的子類 public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
通過Executors的newSingleThreadExecutor方法來創建。這類執行緒池內部只有一個核心執行緒,它確保所有的任務都在同一個執行緒中按順序執行。SingleThreadExecutor的意義在於統一所有的外界任務到一個執行緒中,這使得在這些任務之間不需要處理執行緒同步的問題。
測試示例:
1 private static void testNewScheduledThreadPool() { 2 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); 3 executorService.schedule(new Runnable() { 4 @Override 5 public void run() { 6 System.out.println("執行緒名稱:" + Thread.currentThread().getName() + ",執行:3秒後執行"); 7 } 8 }, 3, TimeUnit.SECONDS); 9 executorService.scheduleAtFixedRate(new Runnable() { 10 @Override 11 public void run() { 12 System.out.println("執行緒名稱:" + Thread.currentThread().getName() + ",執行:延遲2秒後每3秒執行一次"); 13 } 14 }, 2, 3, TimeUnit.SECONDS); 15 for (int i = 0; i < 5; i++) { 16 final int finalI = i; 17 executorService.execute(new Runnable() { 18 @Override 19 public void run() { 20 System.out.println("執行緒名稱:" + Thread.currentThread().getName() + ",執行:普通任務-" + finalI); 21 } 22 }); 23 } 24 }
輸出結果:
執行緒名稱:pool-1-thread-1,執行:普通任務-0
執行緒名稱:pool-1-thread-2,執行:普通任務-1
執行緒名稱:pool-1-thread-3,執行:普通任務-2
執行緒名稱:pool-1-thread-4,執行:普通任務-3
執行緒名稱:pool-1-thread-5,執行:普通任務-4
執行緒名稱:pool-1-thread-2,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-1,執行:3秒後執行
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
執行緒名稱:pool-1-thread-4,執行:延遲2秒後每3秒執行一次
定時和延遲執行。
九、執行緒池在AsyncTask中的使用
AsyncTask執行非同步任務就是一個很典型的執行緒池使用範例,這裡我們來看看其中是如何使用執行緒池的。
1、執行緒池參數的配置
1 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 2 // We want at least 2 threads and at most 4 threads in the core pool, 3 // preferring to have 1 less than the CPU count to avoid saturating 4 // the CPU with background work 5 private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); 6 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 7 private static final int KEEP_ALIVE_SECONDS = 30; 8 private static final ThreadFactory sThreadFactory = new ThreadFactory() { 9 private final AtomicInteger mCount = new AtomicInteger(1); 10 public Thread newThread(Runnable r) { 11 return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 12 } 13 }; 14 private static final BlockingQueue<Runnable> sPoolWorkQueue = 15 new LinkedBlockingQueue<Runnable>(128); 16 17 public static final Executor THREAD_POOL_EXECUTOR; 18 static { 19 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 20 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, 21 sPoolWorkQueue, sThreadFactory); 22 threadPoolExecutor.allowCoreThreadTimeOut(true); 23 THREAD_POOL_EXECUTOR = threadPoolExecutor; 24 } 25 ......
2、AsyncTask執行任務使用範例
一般使用AsyncTask執行任務的時候,使用方式如下:
new AsyncTask<Object, Object, Object>() { @Override protected Object doInBackground(Object[] objects) { return null; } }.execute();
3、AysncTask使用執行緒池執行任務源碼分析
1 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 2 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 3 private static class SerialExecutor implements Executor { 4 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 5 Runnable mActive; 6 public synchronized void execute(final Runnable r) { 7 mTasks.offer(new Runnable() { 8 public void run() { 9 try { 10 r.run(); 11 } finally { 12 scheduleNext(); 13 } 14 } 15 }); 16 if (mActive == null) { 17 scheduleNext(); 18 } 19 } 20 protected synchronized void scheduleNext() { 21 if ((mActive = mTasks.poll()) != null) { 22 THREAD_POOL_EXECUTOR.execute(mActive); 23 } 24 } 25 } 26 27 public final AsyncTask<Params, Progress, Result> execute(Params... params) { 28 return executeOnExecutor(sDefaultExecutor, params); 29 } 30 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) { 31 ...... 32 exec.execute(mFuture); 33 return this; 34 }
第22行就是執行緒池調用了執行任務的方法。