1. 程式人生 > 其它 >android 延時操作_Android執行緒優化之執行緒池的詳解

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個任務,得到的結果:

7705f534ebc90148d4a96a2c029c67e1.png

可以看出,首先他是無序的,其次他每隔兩秒鐘列印兩個,也就驗證了剛才的說法,他執行緒數滿了之後需要等待,就是排隊打飯一樣。

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的輸出也剛好驗證了我的結論:

9e7905762b585065b060d9a1fbaa08bf.png

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執行後得到的結果:

194aebdc2035f67e3cd8dca5a9141846.png

看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);}

這段程式碼比上面的複雜一些,我們看列印:

b42d66a561ef672eec44fae6af2f516e.png

這裡得到的是什麼結論呢?比較多,首先,他是無序的,其次他間隔2s無限重複,並且啟動這個縣城需要3s,也就是延遲的1s + sleep的3s。

到這裡我相信大家還是比較清晰的認知執行緒池,但是執行緒池是結合實戰的,不然的話也都是紙上談兵,但是一般執行緒池也只是替換new Thread的操作,所以示例的話,大家可以自行尋找一下,後續的實戰專案文章,我再帶領大家學習執行緒池。

對Android有興趣可以關注我的公眾號【劉桂林】