該篇文章目錄
1.使用線程池原因
2.Thread
3.線程池
4.線程池工廠提供的四種線程池
5.總結
一.使用線程池原因
參考:http://blog.csdn.net/mine_song/article/details/70948223
https://blog.csdn.net/seu_calvin/article/details/52415337
剛開始學習異步任務,當然是用Thread+handler進行異步任務,但是有時bug多和難以維護,我們引入線程池。
二.Thread
- Thread的使用
new Thread(new Runnable(){ @override public void run(){ } }).start();
new Thread(Runnable runnable).start(); new Thread(FutureTask<V>{Callable callable}).start();
處理時間=T1(線程創建時間)+T2(線程處理任務時間)+T3(線程銷毀時間)
- new Thread的弊端
- 每次new Thread新建對象性能差。
- 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統資源導致死機或oom。
- 缺乏更多功能,如定時執行、定期執行、線程中斷。
三.線程池
1.狹義上的線程池
線程池是一種多線程處理的方式,處理過程中將任務加入到任務隊列,然后創建線程進行執行,線程池中所有線程都是后台線程,並且默認是優先級下運。
如果當前是線程空閑狀態,一有任務添加則讓空閑線程執行。
如果當前所有線程都處於繁忙狀態,則讓任務掛起,直到直到線程池數量不再是最大值。
2.廣義上的線程池
多線程技術主要解決處理器單元內多個線程執行的問題。可以明顯減少處理器單元閑置的時間,增加處理器單元的吞吐量。
3.線程池的優勢
-
- 通過對線程進行緩存,減少對象創建、銷毀的時間,性能佳。
- 可有效控制最大並發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
- 提供定時執行、定期執行、單線程、並發數控制等功能。
4.重要的幾個類:
Java里面線程池的頂級接口是Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。
| ExecutorService |
真正的線程池接口。 |
| ScheduledExecutorService |
能和Timer/TimerTask類似,解決那些需要任務重復執行的問題。 |
| ThreadPoolExecutor |
ExecutorService的默認實現。 |
| ScheduledThreadPoolExecutor |
繼承ThreadPoolExecutor的ScheduledExecutorService接口實現,周期性任務調度的類實現。 |
5.使用
1.執行:
Future future = executor.submit(Runnable runnable);
Future future = executor.submit(Callable callable);,返回的future有意義
Future future = executor.execute(Runnable runnable);返回的future沒有意義
2.銷毀:
executor.shutdown();
-
-
shutdown()-
will just tell the executor service that it can't accept new tasks, but the already submitted tasks continue to run
-
-
shutdownNow()-
will do the same AND will try to cancel the already submitted tasks by interrupting the relevant threads. Note that if your tasks ignore the interruption,
shutdownNowwill behave exactly the same way asshutdown.
-
-
5.自定義線程池參數:
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters and default thread factory and rejected execution handler. * It may be more convenient to use one of the {@link Executors} factory * methods instead of this general purpose constructor. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
參數說明如下:
-
- int corePoolSize: 核心線程數。
- int maximumPoolSize: 最大線程數,當任務進來時可以擴充的線程最大值,當大於了這個值就會根據丟棄處理機制來處理
- long keepAliveTime: 作用於非核心線程。當線程數大於
corePoolSize時,多余的空閑線程的最大存活時間,類似於HTTP中的Keep-alive - TimeUnit unit:
時間單位,一般用秒
-
- BlockingQueue<Runnable> workQueue:
//緩沖任務隊列,線程池的execute方法會將Runnable對象存儲起來.
//工作隊列,先進先出,可以看出並不像Picasso那樣設置優先隊列。詳細鏈接:阻塞隊列詳解
-
- ThreadFactory threadFactory:
//線程工廠接口,只有一個new Thread(Runnable r)方法,可為線程池創建新線程
- ThreadFactory threadFactory:
(1)當currentSize<corePoolSize時,沒什么好說的,直接啟動一個核心線程並執行任務。
(2)當currentSize>=corePoolSize、並且workQueue未滿時,添加進來的任務會被安排到workQueue中等待執行。
(3)當workQueue已滿,但是currentSize<maximumPoolSize時,會立即開啟一個非核心線程來執行任務。
(4)當currentSize>=corePoolSize、workQueue已滿、並且currentSize>maximumPoolSize時,調用handler默認拋出RejectExecutionExpection異常。

四.線程池工廠提供的四種線程池
1.定義
- newFixedThreadPool
創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。阻塞隊列為LinkedBlockingQueue
- newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。阻塞隊列為SynchronousQueue
- newScheduledThreadPool
創建一個定長線程池,支持定時及周期性任務執行。阻塞隊列為DelayedWorkQueue
- newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。阻塞隊列為LinkedBlockingQueue
2.四種線程池構造函數
四種線程池的構造器就是在ThreadPoolExecutor的基礎上進行封裝的。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
(一堆人排隊上公廁)
1)從配置參數來看,FixThreadPool只有核心線程,並且數量固定的,也不會被回收,所有線程都活動時,因為隊列沒有限制大小,新任務會等待執行。
(2)【前方高能,筆者腦洞】FixThreadPool其實就像一堆人排隊上公廁一樣,可以無數多人排隊,但是廁所位置就那么多,而且沒人上時,廁所也不會被拆遷,哈哈o(∩_∩)o ,很形象吧。
(3)由於線程不會回收,FixThreadPool會更快地響應外界請求,這也很容易理解,就好像有人突然想上廁所,公廁不是現用現建的。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
(一堆人去一家很大的咖啡館喝咖啡)
(1)CachedThreadPool只有非核心線程,最大線程數非常大,所有線程都活動時,會為新任務創建新線程,否則利用空閑線程(60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務。
(2)任務隊列SynchronousQueue相當於一個空集合,導致任何任務都會被立即執行。
(3)【前方高能,筆者腦洞】CachedThreadPool就像是一堆人去一個很大的咖啡館喝咖啡,里面服務員也很多,隨時去,隨時都可以喝到咖啡。但是為了響應國家的“光盤行動”,一個人喝剩下的咖啡會被保留60秒,供新來的客人使用,哈哈哈哈哈,好惡心啊。如果你運氣好,沒有剩下的咖啡,你會得到一杯新咖啡。但是以前客人剩下的咖啡超過60秒,就變質了,會被服務員回收掉。
(4)比較適合執行大量的耗時較少的任務。喝咖啡人挺多的,喝的時間也不長。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
(4個里面唯一一個有延遲執行和周期重復執行的線程池)
1)核心線程數固定,非核心線程(閑着沒活干會被立即回收)數沒有限制。
(2)從上面代碼也可以看出,ScheduledThreadPool主要用於執行定時任務以及有固定周期的重復任務。
/** * Creates a new {@code ScheduledThreadPoolExecutor} with the * given core pool size. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @throws IllegalArgumentException if {@code corePoolSize < 0} */ public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
(公廁里只有一個坑位)
3.四種線程池的使用
1.newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
線程池為無限大,當執行第二個任務時第一個任務已經完成,會復用執行第一個任務的線程,而不用每次新建線程。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(index); } }); }
2.newFixedThreadPool
創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
因為線程池大小為3,每個任務輸出index后sleep 2秒,所以每兩秒打印3個數字。
定長線程池的大小最好根據系統資源進行設置。如Runtime.getRuntime().availableProcessors()。可參考PreloadDataCache。
3.newScheduledThreadPool
創建一個定長線程池,支持定時及周期性任務執行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("delay 3 seconds"); } }, 3, TimeUnit.SECONDS);
表示延遲3秒執行。
scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS);
表示延遲1秒后每3秒執行一次。
4.newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
結果依次輸出,相當於順序執行各個任務
五.總結
現行大多數GUI程序都是單線程的。Android中單線程可用於數據庫操作,文件操作,應用批量安裝,應用批量刪除等不適合並發但可能IO阻塞性及影響UI線程響應的操作。
不過最近有專門的Rxjava框架應對Thread+Handler的多線程管理問題。
