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行就是線程池調用了執行任務的方法。

【多線程】Android多線程學習筆記——線程池的更多相關文章

  1. Java學習筆記 線程池使用及詳解

    有點笨,參考了好幾篇大佬們寫的文章才整理出來的筆記.... 字面意思上解釋,線程池就是裝有線程的池,我們可以把要執行的多線程交給線程池來處理,和連接池的概念一樣,通過維護一定數量的線程池來達到多個線程 ...

  2. java學習筆記 - 線程池(一)

    線程池(Thread Pool):把一個或多個線程通過統一的方式進行調度和重複使用的技術,避免了因為線程過多而帶來使用上的開銷 優點:(面試題)可重複使用已有線程,避免對象創建.消亡和過度切換的性能開 ...

  3. JAVA基礎再回首(二十五)——Lock鎖的使用、死鎖問題、多線程生產者和消費者、線程池、匿名內部類使用多線程、定時器、面試題

    JAVA基礎再回首(二十五)--Lock鎖的使用.死鎖問題.多線程生產者和消費者.線程池.匿名內部類使用多線程.定時器.面試題 版權聲明:轉載必須注明本文轉自程序猿杜鵬程的博客:http://blog ...

  4. JAVA 多線程和並發學習筆記(三)

    Java並發編程中使用Executors類創建和管理線程的用法 1.類 Executors Executors類可以看做一個“工具類”.援引JDK1.6 API中的介紹: 此包中所定義的 Execut ...

  5. Android自動化學習筆記:編寫MonkeyRunner脚本的幾種方式

    ---------------------------------------------------------------------------------------------------- ...

  6. Android自動化學習筆記之MonkeyRunner:官方介紹和簡單實例

    ---------------------------------------------------------------------------------------------------- ...

  7. android開發學習筆記000

    使用書籍:<瘋狂android講義>——李剛著,2011年7月出版 雖然現在已2014,可我挑來跳去,還是以這本書開始我的android之旅吧. “瘋狂源自夢想,技術成就輝煌.” 讓我這個 ...

  8. Android動畫學習筆記-Android Animation

    Android動畫學習筆記-Android Animation   3.0以前,android支持兩種動畫模式,tween animation,frame animation,在android3.0中 ...

  9. Android 數字簽名學習筆記

    Android 數字簽名學習筆記 在Android系統中,所有安裝到系統的應用程序都必有一個數字證書,此數字證書用於標識應用程序的作者和在應用程序之間建立信任關系,如果一個permission的pro ...

  10. CNN學習筆記:池化層

    CNN學習筆記:池化層 池化 池化(Pooling)是卷積神經網絡中另一個重要的概念,它實際上是一種形式的降采樣.有多種不同形式的非線性池化函數,而其中“最大池化(Max pooling)”是最為常見 ...

隨機推薦

  1. SQLServer的學習場景(關於row_number()和COALESCE()的使用)

    --使用Sql語句,統計出每輛汽車每天行駛的裏程數(不是總裏程) 以下為脚本 CREATE TABLE [dbo].[CarData]([CarID] [int] NULL,[Mileage] [in ...

  2. Django數據庫操作

    剛學到Django的數據庫操作,把它記錄下來,方便以後查看: 在Django中定義數據庫錶,並使用Django提供的方法來對數據庫進行操作(增.删.改.查) 1.定義3個數據庫錶: class Gro ...

  3. 從數列1,2,3.......n 中 隨意取幾個數,使其和等於 m

    //從數列1,2,3.......n 中 隨意取幾個數,使其和等於 m           public static void Print(int n, int m, List<int> ...

  4. Codeforces #250 (Div. 2) C.The Child and Toy

    之前一直想著建圖...遍曆 可是推例子都不正確 後來看數據好像看出了點規律 就抱著試一試的心態水了一下 就....過了..... 後來想想我的思路還是對的 先抽象當前僅僅有兩個點相連 想要拆分耗費最小 ...

  5. Swift之基礎知識

    Swift之基礎知識 出於對Swift3.0的學習,寫下這篇基本語法的筆記.希望能幫助記憶 -0- 這邊提供Swift3.0中文教材,資源鏈接: https://pan.baidu.com/s/1c2 ...

  6. 字母A-Z寫法

    #大寫的a-z,ASCII編碼 65..90|%{[char]$_}     #小寫的A-Z  97..122|%{[char]$_}    方法二: ([char[]](97..122) -as [ ...

  7. XFire中Services.xml 配置的一些細節

    昨天第一次調XFire引擎,放在Tomcat裏之後老是報錯,檢查了很久沒有發現什麼問題,一直很費解. 後來在網上看到一篇文章<XFire services.xml 配置文件補充說明>,根絕 ...

  8. Hibernate學習筆記-Hibernate HQL查詢

    Session是持久層操作的基礎,相當於JDBC中的Connection,通過Session會話來保存.更新.查找數據.session是Hibernate運作的中心,對象的生命周期.事務的管理.數據庫 ...

  9. C# 縮放圖片

    using System; using System.Collections.Generic;using System.Linq;using System.Web;using System.Drawi ...

  10. Android 布局之LinearLayout 子控件weight權重的作用詳析(轉)

    關於Android開發中的LinearLayout子控件權重android:layout_weigh參數的作用,網上關於其用法有兩種截然相反說法: 說法一:值越大,重要性越高,所占用的空間越大: 說法 ...