Android性能優化之使用線程池處理異步任務

原文鏈接:http://blog.csdn.net/u010687392/article/details/49850803

說到線程,我想大家都不陌生,因為在開發時候或多或少都會用到線程,而通常創建線程有兩種方式:

1、繼承Thread類
2、實現Runnable接口

雖說這兩種方式都可以創建出一個線程,不過它們之間還是有一點區別的,主要區別在于在多線程訪問同一資源的情況下,用Runnable接口創建的線程可以處理同一資源,而用Thread類創建的線程則各自獨立處理,各自擁有自己的資源。
所以,在Java中大多數多線程程序都是通過實現Runnable來完成的,而對于Android來說也不例外,當涉及到需要開啟線程去完成某件事時,我們都會這樣寫:

new Thread(new Runnable() { 
    @Override 
    public void run() { 
     //do sth . 
    }
}).start();

這段代碼創建了一個線程并執行,它在任務結束后GC會自動回收該線程,一切看起來如此美妙,是的,它在線程并發不多的程序中確實不錯,而假如這個程序有很多地方需要開啟大量線程來處理任務,那么如果還是用上述的方式去創建線程處理的話,那么將導致系統的性能表現的非常糟糕,更別說在內存有限的移動設備上,主要的影響如下:

1、線程的創建和銷毀都需要時間,當有大量的線程創建和銷毀時,那么這些時間的消耗則比較明顯,將導致性能上的缺失
2、大量的線程創建、執行和銷毀是非常耗cpu和內存的,這樣將直接影響系統的吞吐量,導致性能急劇下降,如果內存資源占用的比較多,還很可能造成OOM
3、大量的線程的創建和銷毀很容易導致GC頻繁的執行,從而發生內存抖動現象,而發生了內存抖動,對于移動端來說,最大的影響就是造成界面卡頓

而針對上述所描述的問題,解決的辦法歸根到底就是:重用已有的線程,從而減少線程的創建。所以這就涉及到線程池(ExecutorService)的概念了,線程池的基本作用就是進行線程的復用,下面將具體介紹線程池的使用

ExecutorService
通過上述分析,我們知道了通過new Thread().start()方式創建線程去處理任務的弊端,而為了解決這些問題,Java為我們提供了ExecutorService線程池來優化和管理線程的使用

使用線程池管理線程的優點

1、線程的創建和銷毀由線程池維護,一個線程在完成任務后并不會立即銷毀,而是由后續的任務復用這個線程,從而減少線程的創建和銷毀,節約系統的開銷
2、線程池旨在線程的復用,這就可以節約我們用以往的方式創建線程和銷毀所消耗的時間,減少線程頻繁調度的開銷,從而節約系統資源,提高系統吞吐量
3、在執行大量異步任務時提高了性能
4、Java內置的一套ExecutorService線程池相關的api,可以更方便的控制線程的最大并發數、線程的定時任務、單線程的順序執行等

ExecutorService簡介

通常來說我們說到線程池第一時間想到的就是它:ExecutorService,它是一個接口,其實如果要從真正意義上來說,它可以叫做線程池的服務,因為它提供了眾多接口api來控制線程池中的線程,而真正意義上的線程池就是:ThreadPoolExecutor,它實現了ExecutorService接口,并封裝了一系列的api使得它具有線程池的特性,其中包括工作隊列、核心線程數、最大線程數等。

線程池:ThreadPoolExecutor

既然線程池就是ThreadPoolExecutor,所以我們要創建一個線程池只需要new ThreadPoolExecutor(…);就可以創建一個線程池,而如果這樣創建線程池的話,我們需要配置一堆東西,非常麻煩,我們可以看一下它的構造方法就知道了:

public ThreadPoolExecutor(int corePoolSize, 
                              int maximumPoolSize, 
                              long keepAliveTime, 
                              TimeUnit unit, 
                              BlockingQueue<Runnable> workQueue, 
                              ThreadFactory threadFactory, 
                              RejectedExecutionHandler handler) 
                              {...}

所以,官方也不推薦使用這種方法來創建線程池,而是推薦使用Executors的工廠方法來創建線程池,Executors類是官方提供的一個工廠類,它里面封裝好了眾多功能不一樣的線程池,從而使得我們創建線程池非常的簡便,主要提供了如下五種功能不一樣的線程池:

1、newFixedThreadPool() :
作用:該方法返回一個固定線程數量的線程池,該線程池中的線程數量始終不變,即不會再創建新的線程,也不會銷毀已經創建好的線程,自始自終都是那幾個固定的線程在工作,所以該線程池可以控制線程的最大并發數。栗子:假如有一個新任務提交時,線程池中如果有空閑的線程則立即使用空閑線程來處理任務,如果沒有,則會把這個新任務存在一個任務隊列中,一旦有線程空閑了,則按FIFO方式處理任務隊列中的任務。
2、newCachedThreadPool() :
作用:該方法返回一個可以根據實際情況調整線程池中線程的數量的線程池。即該線程池中的線程數量不確定,是根據實際情況動態調整的。栗子:假如該線程池中的所有線程都正在工作,而此時有新任務提交,那么將會創建新的線程去處理該任務,而此時假如之前有一些線程完成了任務,現在又有新任務提交,那么將不會創建新線程去處理,而是復用空閑的線程去處理新任務。那么此時有人有疑問了,那這樣來說該線程池的線程豈不是會越集越多?其實并不會,因為線程池中的線程都有一個“保持活動時間”的參數,通過配置它,如果線程池中的空閑線程的空閑時間超過該“保存活動時間”則立刻停止該線程,而該線程池默認的“保持活動時間”為60s。
3、newSingleThreadExecutor() :
作用:該方法返回一個只有一個線程的線程池,即每次只能執行一個線程任務,多余的任務會保存到一個任務隊列中,等待這一個線程空閑,當這個線程空閑了再按FIFO方式順序執行任務隊列中的任務。
4、newScheduledThreadPool() :
作用:該方法返回一個可以控制線程池內線程定時或周期性執行某任務的線程池。
5、newSingleThreadScheduledExecutor() :
作用:該方法返回一個可以控制線程池內線程定時或周期性執行某任務的線程池。只不過和上面的區別是該線程池大小為1,而上面的可以指定線程池的大小。

好了,寫了一堆來介紹這五種線程池的作用,接下來就是獲取這五種線程池,通過Executors的工廠方法來獲?。?/p>

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();

我們可以看到通過Executors的工廠方法來創建線程池極其簡便,其實它的內部還是通過new ThreadPoolExecutor(…)的方式創建線程池的,我們看一下這些工廠方法的內部實現:

public static ExecutorService newFixedThreadPool(int nThreads) { 
            return new ThreadPoolExecutor(nThreads,nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
public static ExecutorService newSingleThreadExecutor() { 
            return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}
public static ExecutorService newCachedThreadPool() { 
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}

我們可以清楚的看到這些方法的內部實現都是通過創建一個ThreadPoolExecutor對象來創建的,正所謂萬變不離其宗,所以我們要了解線程池還是得了解ThreadPoolExecutor這個線程池類,其中由于和定時任務相關的線程池比較特殊(newScheduledThreadPool()、newSingleThreadScheduledExecutor()),它們創建的線程池內部實現是由ScheduledThreadPoolExecutor這個類實現的,而ScheduledThreadPoolExecutor是繼承于ThreadPoolExecutor擴展而成的,所以本質還是一樣的,只不過多封裝了一些定時任務相關的api,所以我們主要就是要了解ThreadPoolExecutor,從構造方法開始:

public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory, RejectedExecutionHandler handler) 
                          {//...}

我們可以看到它構造方法的參數比較多,有七個,下面一一來說明這些參數的作用:

corePoolSize:線程池中的核心線程數量
maximumPoolSize:線程池中的最大線程數量
keepAliveTime:這個就是上面說到的“保持活動時間“,上面只是大概說明了一下它的作用,不過它起作用必須在一個前提下,就是當線程池中的線程數量超過了corePoolSize時,它表示多余的空閑線程的存活時間,即:多余的空閑線程在超過keepAliveTime時間內沒有任務的話則被銷毀。而這個主要應用在緩存線程池中
unit:它是一個枚舉類型,表示keepAliveTime的單位,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)
workQueue:任務隊列,主要用來存儲已經提交但未被執行的任務,不同的線程池采用的排隊策略不一樣,稍后再講
threadFactory:線程工廠,用來創建線程池中的線程,通常用默認的即可
handler:通常叫做拒絕策略,1、在線程池已經關閉的情況下 2、任務太多導致最大線程數和任務隊列已經飽和,無法再接收新的任務 。在上面兩種情況下,只要滿足其中一種時,在使用execute()來提交新的任務時將會拒絕,而默認的拒絕策略是拋一個RejectedExecutionException異常

上面的參數理解起來都比較簡單,不過workQueue這個任務隊列卻要再次說明一下,它是一個BlockingQueue<Runnable>
對象,而泛型則限定它是用來存放Runnable對象的,剛剛上面講了,不同的線程池它的任務隊列實現肯定是不一樣的,所以,保證不同線程池有著不同的功能的核心就是這個workQueue的實現了,細心的會發現在剛剛的用來創建線程池的工廠方法中,針對不同的線程池傳入的workQueue也不一樣,下面我總結一下這五種線程池分別用的是什么BlockingQueue:

1、newFixedThreadPool()—>LinkedBlockingQueue
2、newSingleThreadExecutor()—>LinkedBlockingQueue
3、newCachedThreadPool()—>SynchronousQueue
4、newScheduledThreadPool()—>DelayedWorkQueue
5、newSingleThreadScheduledExecutor()—>DelayedWorkQueue

這些隊列分別表示:

LinkedBlockingQueue:無界的隊列
SynchronousQueue:直接提交的隊列
DelayedWorkQueue:等待隊列

當然實現了BlockingQueue接口的隊列還有:ArrayBlockingQueue(有界的隊列)、PriorityBlockingQueue(優先級隊列)。這些隊列的詳細作用就不多介紹了。

線程池ThreadPoolExecutor的使用
使用線程池,其中涉及到一個極其重要的方法,即:
execute(Runnable command)

該方法意為執行給定的任務,該任務處理可能在新的線程、已入池的線程或者正調用的線程,這由ThreadPoolExecutor的實現決定。

newFixedThreadPool
創建一個固定線程數量的線程池,示例為:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    for (int i = 1; i <= 10; i++) { 
        final int index = i; 
        fixedThreadPool.execute(new Runnable() { 
            @Override 
            public void run() { 
                String threadName = Thread.currentThread().getName();
                Log.v("zxy", "線程:"+threadName+",正在執行第" + index + "個任務"); 
                try { 
                       Thread.sleep(2000); 
                } catch (InterruptedException e) { 
                       e.printStackTrace(); 
                } 
             } 
        }); 
    }

上述代碼,我們創建了一個線程數為3的固定線程數量的線程池,同理該線程池支持的線程最大并發數也是3,而我模擬了10個任務讓它處理,執行的情況則是首先執行前三個任務,后面7個則依次進入任務隊列進行等待,執行完前三個任務后,再通過FIFO的方式從任務隊列中取任務執行,直到最后任務都執行完畢。為了體現出線程的復用,我特地在Log中加上了當前線程的名稱,效果為:


newSingleThreadExecutor
創建一個只有一個線程的線程池,每次只能執行一個線程任務,多余的任務會保存到一個任務隊列中,等待線程處理完再依次處理任務隊列中的任務,示例為:

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        for (int i = 1; i <= 10; i++) {
            final int index = i;
            singleThreadPool.execute(new Runnable() { 
                @Override public void run() { 
                    String threadName = Thread.currentThread().getName(); 
                    Log.v("zxy", "線程:"+threadName+",正在執行第" + index + "個任務"); 
                    try { 
                        Thread.sleep(1000); 
                    } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                    } 
                } 
            });
        }

代碼還是差不多,只不過改了線程池的實現方式,效果我想大家都知道,即依次一個一個的處理任務,而且都是復用一個線程,效果為:


其實我們通過newSingleThreadExecutor()和newFixedThreadPool()的方法發現,創建一個singleThreadExecutorPool實際上就是創建一個核心線程數和最大線程數都為1的fixedThreadPool。

newCachedThreadPool
創建一個可以根據實際情況調整線程池中線程的數量的線程池,示例為:

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 1; i <= 10; i++) {
            final int index = i;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                    Log.v("zxy", "線程:" + threadName + ",正在執行第" + index + "個任務");
                    try {
                        long time = index * 500;
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

為了體現該線程池可以自動根據實現情況進行線程的重用,而不是一味的創建新的線程去處理任務,我設置了每隔1s去提交一個新任務,這個新任務執行的時間也是動態變化的,所以,效果為:


newScheduledThreadPool
創建一個可以定時或者周期性執行任務的線程池,示例為:

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        //延遲2秒后執行該任務
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
            }
        }, 2, TimeUnit.SECONDS);
        //延遲1秒后,每隔2秒執行一次該任務
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
            }
        }, 1, 2, TimeUnit.SECONDS);

newSingleThreadScheduledExecutor
創建一個可以定時或者周期性執行任務的線程池,該線程池的線程數為1,示例為:

        ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();
        //延遲1秒后,每隔2秒執行一次該任務
        singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                Log.v("zxy", "線程:" + threadName + ",正在執行");
            }
        }, 1, 2, TimeUnit.SECONDS);

實際上這個和上面的沒什么太大區別,只不過是線程池內線程數量的不同,效果為:

每隔2秒就會執行一次該任務

自定義線程池ThreadPoolExecutor

Java內置只為我們提供了五種常用的線程池,一般來說這足夠用了,不過有時候我們也可以根據需求來自定義我們自己的線程池,而要自定義不同功能的線程池,上面我們也說了線程池功能的不同歸根到底還是內部的BlockingQueue實現不同,所以,我們要實現我們自己相要的線程池,就必須從BlockingQueue的實現上做手腳,而上面也說了BlockingQueue的實現類有多個,那么這次我們就選用PriorityBlockingQueue來實現一個功能是按任務的優先級來處理的線程池。
1、首先我們創建一個基于PriorityBlockingQueue實現的線程池,為了測試方便,我這里把核心線程數量設置為3,如下:
ExecutorService priorityThreadPool = new ThreadPoolExecutor(3,3,0L,TimeUnit.SECONDS,new PriorityBlockingQueue<Runnable>());
2、然后創建一個實現Runnable接口的類,并向外提供一個抽象方法供我們實現自定義功能,并實現Comparable接口,實現這個接口主要就是進行優先級的比較,代碼如下:

  public abstract class PriorityRunnable implements Runnable, Comparable<PriorityRunnable> {
            private int priority;

            public PriorityRunnable(int priority) {
                if (priority < 0) throw new IllegalArgumentException();
                this.priority = priority;
            }

            @Override
            public int compareTo(PriorityRunnable another) {
                int my = this.getPriority();
                int other = another.getPriority();
                return my < other ? 1 : my > other ? -1 : 0;
            }

            @Override
            public void run() {
                doSth();
            }

            public abstract void doSth();

            public int getPriority() {
                return priority;
            }
        }

3、使用我們自己的PriorityRunnable提交任務,整體代碼如下:

        ExecutorService priorityThreadPool = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
        for (int i = 1; i <= 10; i++) {
            final int priority = i;
            priorityThreadPool.execute(new PriorityRunnable(priority) {
                @Override
                public void doSth() {
                    String threadName = Thread.currentThread().getName();
                    Log.v("zxy", "線程:" + threadName + ",正在執行優先級為:" + priority + "的任務");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

測試效果
我們看下剛剛自定義的線程池是否達到了我們想要的功能,即根據任務的優先級進行優先處理任務,效果如下:


可以從執行結果中看出,由于核心線程數設置為3,剛開始時,系統有3個空閑線程,所以無須使用任務隊列,而是直接運行前三個任務,而后面再提交任務時由于當前沒有空閑線程所以加入任務隊列中進行等待,此時,由于我們的任務隊列實現是由PriorityBlockingQueue實現的,所以進行等待的任務會經過優先級判斷,優先級高的放在隊列前面先處理。從效果圖中也可以看到后面的任務是先執行優先級高的任務,然后依次遞減。

優先級線程池的優點

從上面我們可以得知,創建一個優先級線程池非常有用,它可以在線程池中線程數量不足或系統資源緊張時,優先處理我們想要先處理的任務,而優先級低的則放到后面再處理,這極大改善了系統默認線程池以FIFO方式處理任務的不靈活

擴展線程池ThreadPoolExecutor

除了內置的功能外,ThreadPoolExecutor也向外提供了三個接口供我們自己擴展滿足我們需求的線程池,這三個接口分別是:

beforeExecute() - 任務執行前執行的方法
afterExecute() -任務執行結束后執行的方法
terminated() -線程池關閉后執行的方法

這三個方法在ThreadPoolExecutor內部都沒有實現
前面兩個方法我們可以在ThreadPoolExecutor內部的runWorker()方法中找到,而runWorker()是ThreadPoolExecutor的內部類Worker實現的方法,Worker它實現了Runnable接口,也正是線程池內處理任務的工作線程,而Worker.runWorker()方法則是處理我們所提交的任務的方法,它會同時被多個線程訪問,所以我們看runWorker()方法的實現,由于涉及到多個線程的異步調用,必然是需要使用鎖來處理,而這里使用的是Lock來實現的,我們來看看runWorker()方法內主要實現:


可以看到在task.run()之前和之后分別調用了beforeExecute和afterExecute方法,并傳入了我們的任務Runnable對象
而terminated()則是在關閉線程池的方法中調用,而關閉線程池有兩個方法,我貼其中一個:
這里寫圖片描述

所以,我們要擴展線程池,只需要重寫這三個方法,并實現我們自己的功能即可,這三個方法分別都會在任務執行前調用、任務執行完成后調用、線程池關閉后調用。這里我驗證一下,繼承自ThreadPoolExecutor 并實現那三個方法:

        public class MyThreadPoolExecutor extends ThreadPoolExecutor {
            public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
                super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
            }

            @Override
            protected void beforeExecute(Thread t, Runnable r) {
                super.beforeExecute(t, r);
                String threadName = t.getName();
                Log.v("zxy", "線程:" + threadName + "準備執行任務!");
            }

            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                String threadName = Thread.currentThread().getName();
                Log.v("zxy", "線程:" + threadName + "任務執行結束!");
            }

            @Override
            protected void terminated() {
                super.terminated();
                Log.v("zxy", "線程池結束!");
            }
        }

而運行后的結果則是,這正符合剛剛說的:

11-17 05:47:51.184 1602-1619/? V/zxy: 線程:pool-6-thread-1準備執行任務!
11-17 05:47:51.184 1602-1619/? V/zxy: 線程:pool-6-thread-1正在執行任務!
11-17 05:47:53.184 1602-1619/? V/zxy: 線程:pool-6-thread-1任務執行結束!
11-17 05:47:58.896 1602-1619/? V/zxy: 線程池結束!

所以,在上面我們的優先級線程池的代碼上,我們再擴展一個具有暫停功能的優先級線程池,代碼如下:具有暫時功能的線程池:

public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
    private boolean isPaused;
    private ReentrantLock pauseLock = new ReentrantLock();
    private Condition unpaused = pauseLock.newCondition();

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        pauseLock.lock();
        try {
            while (isPaused) unpaused.await();
        } catch (InterruptedException ie) {
            t.interrupt();
        } finally {
            pauseLock.unlock();
        }
    }

    public void pause() {
        pauseLock.lock();
        try {
            isPaused = true;
        } finally {
            pauseLock.unlock();
        }
    }

    public void resume() {
        pauseLock.lock();
        try {
            isPaused = false;
            unpaused.signalAll();
        } finally {
            pauseLock.unlock();
        }
    }
}

然后結合上面的優先級線程池的實現,創建具有暫停功能的優先級線程池:

  PausableThreadPoolExecutor pausableThreadPoolExecutor = new PausableThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
        for (int i = 1; i <= 100; i++) {
            final int priority = i;
            pausableThreadPoolExecutor.execute(new PriorityRunnable(priority) {
                @Override
                public void doSth() {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(priority + "");
                        }
                    });
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

這里我為了演示效果,把這個線程池設為只有一個線程,然后直接在TextView中顯示當前執行的任務的優先級,然后設置個開關,控制線程池的暫停與開始:

        if (isPause) {
            pausableThreadPoolExecutor.resume();
            isPause = false;
        } else {
            pausableThreadPoolExecutor.pause();
            isPause = true;
        }

效果為:


從效果上來看,該線程池和優先級線程一樣,而且還多了一個暫停與開始的功能

優化線程池ThreadPoolExecutor

雖說線程池極大改善了系統的性能,不過創建線程池也是需要資源的,所以線程池內線程數量的大小也會影響系統的性能,大了反而浪費資源,小了反而影響系統的吞吐量,所以我們創建線程池需要把握一個度才能合理的發揮它的優點,通常來說我們要考慮的因素有CPU的數量、內存的大小、并發請求的數量等因素,按需調整。
通常核心線程數可以設為CPU數量+1,而最大線程數可以設為CPU的數量*2+1。
獲取CPU數量的方法為:
Runtime.getRuntime().availableProcessors();

shutdown()和shutdownNow()的區別

關于線程池的停止,ExecutorService為我們提供了兩個方法:shutdown和shutdownNow,這兩個方法各有不同,可以根據實際需求方便的運用,如下:

1、shutdown()方法在終止前允許執行以前提交的任務。
2、shutdownNow()方法則是阻止正在任務隊列中等待任務的啟動并試圖停止當前正在執行的任務。

關于AsyncTask的實現

大家都知道AsyncTask內部實現其實就是Thread+Handler。其中Handler是為了處理線程之間的通信,而這個Thread到底是指什么呢?通過AsyncTask源碼可以得知,其實這個Thread是線程池,AsyncTask內部實現了兩個線程池,分別是:串行線程池和固定線程數量的線程池。而這個固定線程數量則是通過CPU的數量決定的。
在默認情況下,我們大都通過AsyncTask::execute()來執行任務的,,而execute()內部則是調用executeOnExecutor(sDefaultExecutor, params)方法執行的,第一個參數就是指定處理該任務的線程池,而默認情況下AsyncTask是傳入串行線程池(在這里不講版本的變化),也就是任務只能單個的按順序執行,而我們要是想讓AsyncTask并行的處理任務,大家都知道調用AsyncTask::executeOnExecutor(sDefaultExecutor, params)方法傳入這個參數即可:AsyncTask.THREAD_POOL_EXECUTOR。而這個參數的意義在于為任務指定了一個固定線程數量的線程池去處理,從而達到了并行處理的功能,我們可以在源碼中看到AsyncTask.THREAD_POOL_EXECUTOR這個參數就是一個固定線程數量的線程池:

public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容