android線程池的使用方法和原理

線程是操作系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制的產生,並且線程的創建和銷燬都會有相應的開銷。當系統中存在大量的線程時,系統會通過時間片輪轉的方式調度每個線程,因此線程不可能做到絕對的並行,除非CPU核心數>線程數,一般來說是不可能的。試想一下如果在一個進程中頻繁的創建和銷燬線程,顯然不是高效的做法。正確的方法是使用線程池,一個線程池會緩存一定數量的線程,做到線程重用、有效控制最大併發數,避免因爲頻繁創建和銷燬線程帶來的系統開銷。


如何創建並配置線程池:

線程池的概念來源於Java的Executor,Executor是一個接口,真正的線程池的實現爲ThreadPoolExecutor它的構造方法提供了一系列參數來配置線程池

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

corePoolSize:指線程池的核心線程數,默認情況下核心線程會在線程池中一直存活,即時它們處於閒置狀態。但如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性置爲true,那麼如果核心線程等待的時長超過keepAliveTime,核心線程就會被終止。

maximumPoolSize:線程池所能容納的最大線程數,當活動線程達到這個數值後,後續的新任務將會被阻塞。

keepAliveTime:非核心線程閒置時的超時時長,超過時長就會被回收。allowCoreThreadTimeOut屬性置爲true,那麼這個屬性對核心線程亦有效。

unit:keepAliveTime的時間單位,如TimeUnit.MILLISECONDS(毫秒)。

workQueue:線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這裏。

threadFactory:線程工廠,爲線程池創建新線程。ThreadFactory只有一個方法:Thread newThread(Runnable r)。

RejectedExecutionHandler:當線程池無法執行新任務時,可能是由於任務隊列已滿或無法成功執行任務,這時ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法,默認rejectedExecution會直接拋出一個RejectedExecutionException(走AbortPolicy的實現)。


ThreadPoolExecutor執行任務的規則:

  1. 如果線程池中的線程數量未達到核心線程的數量,那麼會直接創建一個核心線程執行任務。(注意核心線程、非核心線程都是線程池創建的,並非外界賦予線程池的,外界只是賦予任務)

  2. 如果任務無法插入到任務隊列中,往往是因爲任務隊列已滿。這時如果線程數量未達線程池規定的最大值,那麼會立刻創建一個非核心線程來執行任務。(注意任務隊列的長度跟線程池所能容納的最大線程數不是一個概念)

  3. 如果2中線程數量已達線程池規定的最大值,那麼線程池會拒絕執行任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。


AsyncTask內部就是使用了線程池,來看一下AsyncTask是怎樣配置線程池的:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

 private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

可見AsyncTask配置的線程池規格如下:
核心線程數:CPU核心數 + 1
最大線程數:2 * CPU核心數 + 1
非核心線程閒置時間:1秒
任務隊列長度:128


線程池的分類

Android中常見的有4類線程池,它們也是通過配置ThreadPoolExecutor來實現自己的功能特性:

1. FixedThreadPool

特點:線程數量固定,只有核心線程,當線程處於空閒狀態時不會被回收,任務隊列大小無限制。

創建方法:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

2. CachedThreadPool

特點:只有非核心線程,最大線程數可以任意大,所以任何任務都會被立刻執行,空閒線程超時時長爲60秒,適合執行大量的耗時較少的任務。

創建方法:

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

3. ScheduledThreadPool

特點:核心線程數量固定,非核心線程數量無限制,非核心線程空閒時會被立刻回收,適合執行定時任務和具有固定週期的重複任務。

創建方法:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

4. SingleThreadExecutor

特點:內部只有一個核心線程,能確保所有任務在同一個線程中順序執行,任務之間不需處理線程同步的問題。

創建方法:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

使用示例:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        SystemClock.sleep(2000);    
    }
};

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//1秒後執行runnable
scheduledThreadPool.schedule(runnable, 1000, TimeUnit.MILLISECONDS);
//0.5秒後每1秒執行一次runnable
scheduledThreadPool.scheduleAtFixedRate(runnable, 500, 1000, TimeUnit.MILLISECONDS);

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
singleThreadPool.execute(runnable);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章