android 延時操作_Android執行緒優化之執行緒池的詳解
技術標籤:android 延時操作
使用執行緒來進行非同步任務,這對於大多數人而言還是輕車熟路的,而且使用起來比較簡單,只需要通過new Thread() 之後 start 即可,當任務完成之後則會銷燬,但是這裡有一個弊端,就是如果一個高度任務,比如說定時處理非同步事件,如果你每次執行都new Thread的話,這將導致執行緒頻繁的建立與銷燬,這樣會導致佔用大量的資源,這已經就十分的不友好了,而且所有建立的子執行緒都沒有一個統一的管理,如果要優化,也應該從這裡下手了,所以,我們,我們就更應該來了解一下執行緒池的使用了,使用執行緒池可以複用建立的子執行緒,不會反覆建立和銷燬,並且使用起來也不算太難。
ThreadPoolExecutor
我們要理解ThreadPoolExecutor,因為Android中其他幾個執行緒池都是他延伸出來的,相當於他就是個爸爸,所以你要先了解ThreadPoolExecutor的前世今生,首先來看下他的建構函式的幾個引數:
- int corePoolSize
- 核心執行緒數量
- int maximumPoolSize
- 最大執行緒數量
- long keepAliveTime,
- 超時時長
- TimeUnit unit,
- 時間列舉
- BlockingQueue workQueue,
- 任務佇列
- ThreadFactory threadFactory
- 執行緒工廠介面
- RejectedExecutionHandler handler
- 拒絕執行的Handler
這裡要注意,正常情況下,corePoolSize是一直存在的, 並且當 corePoolSize >= maximumPoolSize的時候,執行緒會被阻塞等待任務完成,keepAliveTime觸發的時候非核心執行緒都會被回收掉。
ThreadPoolExecutor還是有很多可講的,但是我選擇的還是著重講解Android中的四個執行緒池,網上對ThreadPoolExecutor的例子太多了,也比較雜,大家可以去看看。
Android中的執行緒池的概念來源於Java中的Executor,Executor是一個介面,真正的執行緒池的實現為ThreadPoolExecutor,其中所實現的四個執行緒池每一個的作用都是不一樣的,我們一起來看下:
FixThreadPool
FixThreadPool通過如下程式碼實現:
ExecutorService service = Executors.newFixedThreadPool(5);Runnable r = new Runnable() {@Override public void run() {Log.i(TAG, "newFixedThreadPool");}};service.execute(r);
佇列執行緒池,因為核心執行緒池是固定的,所以不管你如何execute,都會一個個來執行完成,因為直接建立使用,沒有回收,所以他的優勢是響應速度很快,效率更高。
這裡我們可以模擬一下:
ExecutorService service = Executors.newFixedThreadPool(2);for (int i = 0; i < 10; i++) {final int finalI = i;service.execute(new Runnable() {@Override public void run() {try {Thread.sleep(2000);Log.i(TAG, "newFixedThreadPool:" + finalI );} catch (InterruptedException e) {e.printStackTrace();}}});}
這裡指定核心執行緒數為2,然後執行10個任務,得到的結果:
可以看出,首先他是無序的,其次他每隔兩秒鐘列印兩個,也就驗證了剛才的說法,他執行緒數滿了之後需要等待,就是排隊打飯一樣。
SingleThreadPool
顧名思義,這個都不需要指定核心執行緒數,程式碼如下:
ExecutorService service = Executors.newSingleThreadExecutor();Runnable r = new Runnable() {@Override public void run() {Log.i(TAG, "newSingleThreadExecutor");}};service.execute(r);
這個其實和newFixedThreadPool(1)是一樣的,SingleThreadPool主要還是為了執行緒同步而來的,newFixedThreadPool可以同時執行多工,所以肯定不是同步的,如果什麼場景需要執行緒同步,那SingleThreadPool再合適不過了。
如果我們把上面的例子改成SingleThreadPool:
ExecutorService service = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int finalI = i;service.execute(new Runnable() {@Override public void run() {try {Thread.sleep(2000);Log.i(TAG, "newSingleThreadExecutor:" + finalI );} catch (InterruptedException e) {e.printStackTrace();}}});}
可以得到的結論:首先他是有序的,其次他每隔兩秒則列印一次,而Log的輸出也剛好驗證了我的結論:
CachedThreadPool
CachedThreadPool還是比較多使用的,他不需要指定執行緒數,因為他的執行緒數很大,但是不存在核心執行緒,這也就意味著經常被回收,他的回收思路是,執行任務,任務結束後,保留60s,在60s來了新任務則繼續使用剛才的執行緒,如果60s內無任務則回收執行緒,有點類似吃飯,你去盛飯沒關係,但是你吃完走了我就要收盤子,間隙就在60s
ExecutorService service = Executors.newCachedThreadPool();Runnable r = new Runnable() {@Override public void run() {Log.i(TAG, "newCachedThreadPool");}};service.execute(r);
我們繼續改造我們的示例程式碼:
ExecutorService service = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int finalI = i;service.execute(new Runnable() {@Override public void run() {try {Thread.sleep(2000);Log.i(TAG, "newCachedThreadPool:" + finalI );} catch (InterruptedException e) {e.printStackTrace();}}});}
這段程式碼在前面兩個執行緒池都使用到了,就是用來告訴你他們的區別,我們使用newCachedThreadPool執行後得到的結果:
看Log,我們得到的結論是:首先他也是無序的,並且他因為子執行緒夠多,所以sleep兩秒後直接全部打印出來了。
ScheduledThreadPool
這是一個可操作性較強的執行緒池,也是唯一一個可迴圈的執行緒池,便於一些重複性的工作使用
ScheduledExecutorService service = Executors. newScheduledThreadPool (5);Runnable r = new Runnable() {@Override public void run() {Log.i(TAG, "newScheduledThreadPool");}};Log.i(TAG,"start");service.scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);
scheduleAtFixedRate中有四個引數,第一個是任務,第二個是延時時長,第三個是迴圈時長,第四個是單位,也就是說,當啟動scheduleAtFixedRate之後,在1s後才開始執行這個間隔2s的任務不斷迴圈,可以這樣理解,啟動任務後,這裡延時1s,然後才開始執行任務,以後每隔2s重複執行
我們拿著示例繼續改造:
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);for (int i = 0; i < 10; i++) {final int finalI = i;service.scheduleAtFixedRate(new Runnable() {@Override public void run() {try {Thread.sleep(2000);Log.i(TAG, "newCachedThreadPool:" + finalI );} catch (InterruptedException e) {e.printStackTrace();}}},1000,2000,TimeUnit.MILLISECONDS);}
這段程式碼比上面的複雜一些,我們看列印:
這裡得到的是什麼結論呢?比較多,首先,他是無序的,其次他間隔2s無限重複,並且啟動這個縣城需要3s,也就是延遲的1s + sleep的3s。
到這裡我相信大家還是比較清晰的認知執行緒池,但是執行緒池是結合實戰的,不然的話也都是紙上談兵,但是一般執行緒池也只是替換new Thread的操作,所以示例的話,大家可以自行尋找一下,後續的實戰專案文章,我再帶領大家學習執行緒池。
對Android有興趣可以關注我的公眾號【劉桂林】