經濟觀察
運營管理

android線程池管理

前言

線程池是Java中的一個重要概念,從Android上來說,當我們跟服務端進行數據交互的時候我們都知道主線程不能進行聯網操作以及耗時操作,主線程進行聯網操作在3.0之后會報一個NewWorkOnMainTHreadException的異常,而在主線程進行耗時操作則會引起ANR(Application Not Responding),但是我們經常要跟服務端進行交互,下載和上傳數據等,這也就是進行聯網操作,在初學時我們都是通過new Thread來開啟一個線程進行聯網操作,但是跟服務端交互多了,如果還是使用new Thread()來開啟子線程,在一個應用中我們頻繁的去通過這個方法去開啟線程,這對性能來說是很大的浪費,頻繁的開啟銷毀線程對內存的消耗是很大的,而頻繁的開啟線程也讓整個應用的線程管理顯得很混亂,這是不可取的,這時候使用線程池就可以解決這些問題了,這篇文章我嘗試將線程池概念和應用說清楚,然后封裝一個自定義的線程池策略以后根據業務需求稍微更改下相關的參數即可。


線程池好處

線程池可以解決兩個不同問題:由于減少了每個任務調用的開銷,它們通常可以在執行大量異步任務時提供增強的性能,并且還可以提供綁定和管理資源(包括執行任務集時使用的線程)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計數據,如完成的任務數。 - - - - -Doug Lea(線程池設計者)

從前文也可以得出線程池有下面幾個好處

(1)自定義線程池策略可以重用線程池中的線程,避免應用中頻繁的創建和銷毀線程所造成的內存消耗以及性能上不必要的開銷;

(2)通過控制線程池的最大線程數能有效控制線程池的最大并發數,避免大量的線程同一時間并發搶占系統資源而造成的阻塞和界面卡死;

(3)可以自己管理線程,定時執行,間隔循環執行,線程關閉等,通過對線程的管理可以避免濫用多線程造成的問題,比如內存泄露、界面卡死、CPU阻塞等


ThreadPoolExecutor構造方法解釋

線程池有著很深的體系,如果每個類每個接口都細說顯得不現實,所以這里重講它的實現類,Executor是線程池的頂級接口,而ThreadPoolExecutor是它的實現類,這個類提供了一系列方法來配置我們自己的線程池,它也封裝了一些典型的線程池策略供我們使用.

ThreadPoolExecutor繼承自AbstractExecutorService,而AbstractExecutorService實現了ExecutorService接口,ExecutorService接口繼承了Executor,通過改變ThreadPoolExecutor構造方法里的參數我們可以自己定義線程池的屬性,它一共有四個構造方法,比較常用的是下面這個方法

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory)

但是為了對這個類進行說明我選擇的是參數最多的方法,因為不同構造方法最終回調的都是這個方法,只是設計者幫我做了一些默認的操作所以不用自己設置。

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

corePoolSize

線程池的保存的線程數,它包括空閑線程,上面是api中的解釋,其實詳細了說這是線程池中的核心線程數,在默認情況下,即使線程是空閑狀態,這個核心線程也會在線程池中存在。但是設計進行了一些邏輯處理

/**

* Returns true if this pool allows core threads to time out and

* terminate if no tasks arrive within the keepAlive time, being

* replaced if needed when new tasks arrive. When true, the same

* keep-alive policy applying to non-core threads applies also to

* core threads. When false (the default), core threads are never

* terminated due to lack of incoming tasks.

*

* @return {@code true} if core threads are allowed to time out,

* else {@code false}

*

* @since 1.6

public boolean allowsCoreThreadTimeOut() {

return allowCoreThreadTimeOut;

}

* If false (default), core threads stay alive even when idle.

* If true, core threads use keepAliveTime to time out waiting

* for work.

private volatile boolean allowCoreThreadTimeOut;

從注釋我們可以看出如果把allowCoreThreadTimeOut的屬性值設置為true(默認是false)那么現在空閑的核心線程在等待新任務時會使用一個超時策略,這個時間間隔由keepAliveTime設置,當空閑線程等待的時間超過設置時間時核心線程將被終止。

maximumPoolSize

線程池允許容納的最大線程數,當活動的線程達到這個最大數時如果后續還有任務,那新任務將會被阻塞(通過參數可以設置阻塞時的不同處理策略)。

keepAliveTime

非核心線程閑置時的超時時長,非核心線程可以這么理解,比如核心線程數是2,最大線程數3,那么這三個里面有一個就是非核心線程了,用個比喻就是臨時工,當核心成員干不完任務就會叫上臨時工一起,但是任務完成了臨時工什么時候辭退就是自己指定了(比喻可能不恰當,不帶任何其他的隱喻),回到文章,當超過這個時長時,非核心線程就會被回收,但是剛才介紹corePoolSize的時候也說了,如果把allowCoreThreadTimeOut的屬性設置為true,keepAliveTime也可以同樣對核心線程進行終止操作。

unit

就是keepAliveTime的時間單位,直接用TimeUnit這個枚舉就可以點出來,常用的有TimeUnit.MINUTS(分),TimeUnit.SECONDS(秒),TimeUnit.MILLISECONDS(毫秒).不常用的有TimeUnit.HOURS(小時),TimeUnit.DAYS(天)。

workQueue

線程池中的任務隊列(阻塞隊列),線程池的execute方法提交的Runnable對象存儲在這個隊列里面,BlockingQueue是線程安全的,常用的阻塞隊列有如下五個

(1).LinkedBlockingQueue

鏈表阻塞隊列,這個隊列由一個鏈表構成,它內部維護了一個數據緩存隊列,這個隊列按 FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部 是在隊列中時間最短的元素。新元素插入到隊列的尾部,并且隊列獲取操作會獲得位于隊列頭部的元素。鏈接隊列的吞吐量通常要高于基于數組的隊列,但是在大多數并發應用程序中,其可預知的性能要低。 當構造這個隊列對象時,如果不指定隊列容量大小,它的默認容量大小是Integer.MAX_VALUE(無限大),這樣就是一個無界隊列,除非插入節點會使隊列超出容量,否則每次插入后會動態地創建鏈接節點。

(2).ArrayBlockingQueue

數組阻塞隊列,這是一個有界阻塞隊列內部,維護了一個定長數組用來緩存隊列中的數據對象,通過兩個整型變量來標識隊列的頭跟尾在數組中的位置,這個隊列按 FIFO(先進先出)原則對元素進行排序。隊列的頭部是在隊列中存在時間最長的元素。隊列的尾部 是在隊列中存在時間最短的元素。新元素插入到隊列的尾部,隊列獲取操作則是從隊列頭部開始獲得元素。這是一個典型的“有界緩存區”,固定大小的數組在其中保持生產者插入的元素和使用者提取的元素。一旦創建了這樣的緩存區,就不能再增加其容量。試圖向已滿隊列中放入元素會導致操作受阻塞;試圖從空隊列中提取元素將導致類似阻塞。

此類支持對等待的生產者線程和使用者線程進行排序的可選公平策略。默認情況下,不保證是這種排序。然而,通過將公平性 (fairness) 設置為 true 而構造的隊列允許按照 FIFO 順序訪問線程。公平性通常會降低吞吐量,但也減少了可變性和避免了“不平衡性”。

(3).DelayQueue

延遲隊列,它也是一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。這個隊列的頭部是延遲期滿后保存時間最長的 Delayed 元素。如果延遲都還沒有期滿,則隊列沒有頭部,并且 poll 將返回 null。當一個元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個小于等于 0 的值時,將發生到期。即使無法使用 take 或 poll 移除未到期的元素,也不會將這些元素作為正常元素對待。例如,size 方法同時返回到期和未到期元素的計數。此隊列不允許使用 null 元素。 通常用這個隊列來管理一個超時未響應隊列。

(4).PriorityBlockingQueue

優先阻塞隊列,它也是一個無界阻塞隊列,它使用與類 PriorityQueue 相同的順序規則,并且提供了阻塞獲取操作。雖然這個隊列邏輯上是無界的,但是資源被耗盡時試圖執行 add 操作也將失敗(導致 OutOfMemoryError)。此類不允許使用null(空)元素。依賴自然順序的優先級隊列也不允許插入不可比較的對象(這樣做會導致拋出 ClassCastException)。 在創建隊列對象時通過構造函數中的Comparator屬性對象來決定優先級。

(5).SynchronousQueue

同步阻塞隊列,一種無緩沖阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作 ,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行 peek,因為僅在試圖要移除元素時,該元素才存在;除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)插入元素;也不能迭代隊列,因為其中沒有元素可用于迭代。隊列的頭 是嘗試添加到隊列中的首個已排隊插入線程的元素;如果沒有這樣的已排隊線程,則沒有可用于移除的元素并且 poll() 將會返回 null。對于其他 Collection (集合)方法(例如 contains),同步隊列作為一個空集合這個不允許null(空)元素。

同步隊列類似于 CSP 和 Ada 中使用的 rendezvous 信道。它非常適合于傳遞性設計,在這種設計中,在一個線程中運行的對象要將某些信息、事件或任務傳遞給在另一個線程中運行的對象,它就必須與該對象同步。

對于正在等待的生產者和使用者線程而言,此類支持可選的公平排序策略。默認情況下不保證這種排序。但是,使用公平設置為 true 所構造的隊列可保證線程以 FIFO 的順序進行訪問。

threadFactory

線程工廠,用于在線程池中創建新線程,線程池設計者提供了兩個(其實是一個)線程池工廠給我們使用,一個是defaultThreadFactory(),另一個是privilegedThreadFactory,但是查看源碼我發現

* Legacy security code; do not use.

public static ThreadFactory privilegedThreadFactory() {

return new PrivilegedThreadFactory();

}

設計者說這是遺留的安全代碼,叫我們不要使用privilegedThreadFactory..,所以一般線程池工廠我們用的是defaultThreadFactory,用法很簡單,直接用Executors.defaultThreadFactory();就可以創建一個默認線程池工廠。

handler

這個參數是一個RejectedExecutionHandler對象,它是一個接口,只有rejectedExecution這個方法,這個參數的作用是當線程池由于任務隊列已滿或別的原因無法執行新任務時,ThreadPoolExecutor就會回調實現了這個接口的類來處理被拒絕任務,它的使用是直接用THreadPoolExecutor.XXX,線程池設計者提供了四個處理方法:

(1).ThreadPoolExecutor.AbortPolicy

直接拋出RejectedExecutionException異常,如果不設置處理策略默認是這個方法進行處理

(2).ThreadPoolExecutor.CallerRunsPolicy

直接在 execute 方法的調用線程中運行被拒絕的任務;如果執行程序已關閉,則會丟棄該任務.

(3).ThreadPoolExecutor.DiscardOldestPolicy

放棄最舊的未處理請求,然后重試 execute;如果執行程序已關閉,則會丟棄該任務。

(4).ThreadPoolExecutor.DiscardPolicy

默認情況下直接丟棄被拒絕的任務。

在平常使用時可以根據實際需要制定相關的操作。

小結

THreadPoolExecutor在執行任務時大體是這樣的,如果線程池中線程的數量沒有達到核心線程的數量,它就會啟動一個核心線程來執行任務,如果達到或超過核心線程數量,那么任務會插入到任務隊列中等待執行,如果任務隊列滿了。但是線程數量沒有達到線程池的最大數,那就回啟動一個非核心線程(臨時工)來執行任務,如果線程數量達到線程池最大數,那么就會拒絕處理新任務并調用handler的rejectedExecution來處理拒絕任務(處理策略就是上面四種)。


線程池設計者提供的四種線程池策略

線程池設計者為我們提供了四種不同的線程池策略,分別是FixedThreadPool,SingleThreadExecutor,CachedThreadPool,ScheduledThreadPool,他們放在了Executors這個類中,下面逐一分析

(1).FixedThreadPool

這個策略源碼為:

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue(),

threadFactory);

}

通過Executors.newFixedThreadPool()這個方法我們可以創建這個線程池,這是一個線程數量固定且可重用的線程池它的核心線程數和最大線程數是一樣的,當線程處于空閑狀態時,除非線程池關閉,否則線程不會被回收,,當所有線程都處于活動狀態時,新任務都會處于等待狀態,這個線程池策略沒有超時策略(0L),任務隊列也是無界隊列(LinkedBlockingQueue),線程工廠由調用者自己指定,拒絕任務處理策略是默認處理策略(直接拋出RejectedExecutionException異常)。

(2).SingleThreadExecutor

這個策略源碼為:

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1, 1,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue(),

threadFactory));

}

從返回的參數可以看出這個策略只有一個核心線程,它確保了所有任務都是在同一個線程中按順序來執行,這就不需要處理線程同步的問題了,由于最大線程數跟核心線程數都是1,所以也就不存在非核心線程超時策略了,任務隊列為無界隊列,線程池工廠是默認的線程池工廠。

(3).CachedThreadPool

這個策略源碼為:

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

從返回的參數可以看出,它并沒有核心線程數(0),最大線程數為Integer.MAX_VALUE(可以看做任意大),當線程池的線程都處于活動狀態時,如果有新任務,線程池會創建新的線程來處理新任務,否則就會用閑置線程來處理任務,它的非核心線程超時策略是60秒(60L),超過60秒閑置線程就會被回收,前面說過SynchronousQueue是一個空集合,是無法存儲元素的,這就造成了只要有任務就會被立即執行,這種線程池策略審核執行大量單耗時較少的任務,如果線程池處于閑置狀態,由于有超時策略,線程池中的所有線程都會被停止,這時線程池中沒有任何線程,所以不占用任何系統資源。

(4).ScheduledThreadPool

這個策略源碼為:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {

return new DelegatedScheduledExecutorService

(new ScheduledThreadPoolExecutor(1));

}

public ScheduledThreadPoolExecutor(int corePoolSize) {

super(corePoolSize, Integer.MAX_VALUE,

DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,

new DelayedWorkQueue());

}

從參數可以看出,它核心線程是調用者自己設定的,也就是一個固定的值,但是最大線程數是無限大,而且超時策略是10毫秒(基本就是立即了..)回收,而阻塞隊列是延時隊列,這也說明了這種線程池策略是執行定時或周期重復的任務。

小結

其實當我們前面把ThreadPoolExecutor構造方法進行分析以后理解Doug Lea(線程池設計者)給我們提供的幾種線程池策略就不難了,我個人覺得Doug Lea之所以給我們提供了幾種策略是為了方便我們使用線程池,但是如果我們了解了參數的含義我們完全可以自己定義一個屬于自己的線程池策略。下面我封裝一個自定義的線程池策略。


封裝自己的線程池策略

前面說了Doug Lea(線程池設計者)提供了四種具有不同特性的線程池策略給我們使用,日常開發使用其中一種也是可以的,但是我們既然懂得了原理當然可以自己自定義一個來使用,下面我一步步封裝一個線程池策略然后進行測試(這個策略某些參數參考了AsyncTask)。

首先是線程池代理類,源碼為:

public class ThreadPoolProxy {

private ThreadPoolExecutor executor;

private int corePollSize;//核心線程數

private int maximumPoolSize;//最大線程數

private long keepAliveTime;//非核心線程持續時間

public ThreadPoolProxy(int corePollSize, int maximumPoolSize, long keepAliveTime) {

this.corePollSize = corePollSize;

this.maximumPoolSize = maximumPoolSize;

this.keepAliveTime = keepAliveTime;

}

/**

* 單例,產生線程

@TargetApi(Build.VERSION_CODES.GINGERBREAD)

public void getThreadPoolProxyInstant() {

if (executor == null)

synchronized (ThreadPoolProxy.class) {

if (executor == null) {

BlockingDeque workQueue = new LinkedBlockingDeque(128);//任務隊列容量128

ThreadFactory threadFactory = Executors.defaultThreadFactory();//默認的線程工廠

RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();//拒絕任務時的處理策略,直接拋異常

executor = new ThreadPoolExecutor(corePollSize,

maximumPoolSize,

keepAliveTime,

TimeUnit.SECONDS,

workQueue,

threadFactory,

handler);

}

}

}

/**

* 執行任務

*

* @param task

public void excute(Runnable task) {

getThreadPoolProxyInstant();

executor.execute(task);

}

/**

* 刪除任務

*

* @param task

public boolean remove(Runnable task) {

getThreadPoolProxyInstant();

boolean remove = executor.remove(task);

return remove;

}

}

這里我把非核心線程超時策略單位設置為了秒,任務隊列容量設置為128,線程池工廠設置成了默認工廠,拒絕任務的策略設置成了直接拋出異常。然后剩下的三個參數在工廠類生產線程時進行設置,工廠類代碼為:

public class ThreadPoolFactory {

private static ThreadPoolProxy commonThreadPool;//獲取一個普通的線程池實例

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//獲得CPU數量

private static final int COMMOM_CORE_POOL_SIZE = CPU_COUNT + 1;//根據CPU核數來設定核心線程數

private static final int COMMON_MAXPOOL_SIZE = CPU_COUNT * 2 + 1;//最大線程池數

private static final long COMMON_KEEP_LIVE_TIME = 1L;//持續時間

public static ThreadPoolProxy getCommonThreadPool() {

if (commonThreadPool == null)

synchronized (ThreadPoolFactory.class) {

if (commonThreadPool == null)

commonThreadPool = new ThreadPoolProxy(COMMOM_CORE_POOL_SIZE,

COMMON_MAXPOOL_SIZE, COMMON_KEEP_LIVE_TIME);

}

return commonThreadPool;

}

}

這里的核心線程跟跟最大線程數是根據手機的cpu核心數進行動態設置,然后把使用方法封裝成一個Util類:

public class ThreadPoolUtil {

/**

* 在非UI線程中執行

*

* @param task

public static void runTaskInThread(Runnable task) {

ThreadPoolFactory.getCommonThreadPool().excute(task);

}

public static Handler handler = new Handler();

/**

* 在UI線程中執行

*

* @param task

public static void runTaskInUIThread(Runnable task) {

handler.post(task);

}

/**

* 移除線程池中線程

*

* @param task

public static boolean removeTask(Runnable task) {

boolean remove = ThreadPoolFactory.getCommonThreadPool().remove(task);

return remove;

}

}

通過上面的封裝就可以自定義一個自己的線程池策略了,當然這個策略的參數可以根據實際需要進行配置,我這里是直接參考AsyncTask來配置的。

然后做個栗子來驗證:

我這里是模擬下載,界面截圖如下:

主界面業務邏輯為:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private ProgressBar pb1;

private ProgressBar pb2;

private ProgressBar pb3;

private ProgressBar pb4;

private Button btn1;

private Button btn2;

private Button btn3;

private Button btn4;

private static final int FLAGS1 = 1;

private static final int FLAGS2 = 2;

private static final int FLAGS3 = 3;

private static final int FLAGS4 = 4;

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case FLAGS1:

pb1.setProgress(msg.arg1);

break;

case FLAGS2:

pb2.setProgress(msg.arg1);

break;

case FLAGS3:

pb3.setProgress(msg.arg1);

break;

case FLAGS4:

pb4.setProgress(msg.arg1);

break;

default:

break;

}

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

init();

}

private void init() {

pb1 = (ProgressBar) findViewById(R.id.pb1);

pb2 = (ProgressBar) findViewById(R.id.pb2);

pb3 = (ProgressBar) findViewById(R.id.pb3);

pb4 = (ProgressBar) findViewById(R.id.pb4);

btn1 = (Button) findViewById(R.id.btn1);

btn2 = (Button) findViewById(R.id.btn2);

btn3 = (Button) findViewById(R.id.btn3);

btn4 = (Button) findViewById(R.id.btn4);

btn1.setOnClickListener(this);

btn2.setOnClickListener(this);

btn3.setOnClickListener(this);

btn4.setOnClickListener(this);

}

private Runnable runnable1 = new MRunnable(FLAGS1);

private Runnable runnable2 = new MRunnable(FLAGS2);

private Runnable runnable3 = new MRunnable(FLAGS3);

private Runnable runnable4 = new MRunnable(FLAGS4);

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn1:

updateProcessBar(runnable1);

break;

case R.id.btn2:

updateProcessBar(runnable2);

break;

case R.id.btn3:

updateProcessBar(runnable3);

break;

case R.id.btn4:

updateProcessBar(runnable4);

break;

default:

break;

}

}

private void updateProcessBar(Runnable runnable) {

ThreadPoolUtil.runTaskInThread(runnable);

}

private class MRunnable implements Runnable {

private int mFlags;

public MRunnable(int flags) {

this.mFlags = flags;

}

@Override

public void run() {

for (int i = 0; i

Message msg = mHandler.obtainMessage();

//隨機數,讓下載更真實...

Random rand = new Random();

int randNum = rand.nextInt(100);

SystemClock.sleep(randNum);

msg.what = mFlags;

msg.arg1 = i;

mHandler.sendMessage(msg);

}

}

}

/**

* 退出時清除線程

@Override

protected void onDestroy() {

super.onDestroy();

ThreadPoolUtil.removeTask(runnable1);

ThreadPoolUtil.removeTask(runnable2);

ThreadPoolUtil.removeTask(runnable3);

ThreadPoolUtil.removeTask(runnable4);

}

}

然后為了方便測試,我把核心線程數固定,而不是根據CPU核心數來改變,當我把核心線程數定為1,這時效果為:

可以看到,當我依次啟動1、2、3、4時,由于隊列我們設置成了LinkedBlockingDeque,這個隊列是按 FIFO(先進先出)排序元素的,所以就會按順序來執行,也就是當1執行時其他任務的都在隊列中阻塞,只有1完成了才會往下執行,這也印證了我們關于線程池的的參數屬性講解,這里可能你們會有疑問,前面不是說當任務數超過corePoolSize就會創建新的非核心線程(臨時工)去處理任務嗎,這里怎么沒有這樣?其實創建新的線程是有條件的,這個條件是你的任務隊列已經滿了,這時如果還加新的任務才會去創建非核心線程去處理這個任務,要驗證其實也很簡單,只要把

BlockingDeque workQueue = new LinkedBlockingDeque(128);

這個隊列大小從128改為2,再重新啟動,效果為:

從圖片可以看到,當我依次啟動任務1、2、3、4時,順序是先1啟動,然后2、3阻塞,然后再啟動4,4也跟著一起執行,然后這兩個任務執行完之后才依次執行2、3,這也驗證了我們上面的分析,我啟動任務1,1立即執行,這時任務隊列是沒有任務在等待的,然后我啟動任務2、3,這時任務隊列并沒有滿,所以這兩個任務就在任務隊列中阻塞等待,當我再啟動任務4時,這時任務隊列已經滿了,而線程數量沒達到最大線程數,所以這個策略就新開了一個非核心線程去執行4這個新的任務,然后由于這時沒有新任務,所以總共就有兩個線程去執行任務了,當1,4有一個執行完了,就會去隊列中依次執行2,3。

把任務隊列大小調回128繼續我們的論證,把核心線程數改成2,這時效果為:

可以看到它每次起兩個線程去完成下載任務,然后如果兩個核心線程任務沒結束后續任務會阻塞等待,只有其中一個完成了才會接著執行。

從上面的栗子也可以看出,當我們把核心線程修改是就可以自己定義下載的規則了,平常我們看一些Android市場下載軟件原理用的就是自己的線程池策略。


總結

Android的線程池策略對我們管理應用中的線程是一個很方便高效的事,在日常開發中我們可以根據實際需要去配置符合業務需求的線程池策略,這樣可以讓應用處理涉及耗時的任務時更高效和節省資源開銷,這篇文章我是很用心的寫了,在寫的過程中我自己對某些知識點也是有一種恍然大悟的感覺,所以我很希望這篇文章對想了解這方面知識點的人有所幫助,如果某些地方不了解或覺得哪個點錯了,可以評論(雖然不一定及時看到..)但是有疑問我一定盡力去解答(微笑臉)。


最后雖然所有類都在文章中但是還是附個文章涉及的源碼,可能有些筒子是直接看源碼的(比如我)…

平常一直用svn管理代碼,前不久剛學會怎么使用git(感覺自己有點土逼的感覺),所以這次直接把源碼傳到github上,有些同學可能習慣在csdn上下載,所以多搞了個csdn鏈接,我就是這么人性化(驕傲臉)..年紀大了有些碎碎嘴了..

github地址:http://github.com/songjiran/ThreadPoolDemo

csdn地址:http://download.csdn.net/detail/lxzmmd/9535019

轉載請注明出處經濟管理網 » android線程池管理

相關推薦

    江苏十一选五的任三