Android中的線程池

使用線程池的好處:

  • 重用線程池中的線程,避免因線程的創建和銷燬所帶來的性能開銷.
  • 有效控制線程池的最大併發數,避免大量的線程之間因相互爭搶系統資源而造成堵塞現象.
  • 對線程進行簡單管理,並提供定時執行以及指定間隔循環執行等功能.

Android中的線程池的概念來源於Java中的Executer(接口),實現爲ThreadPoolExecutor.提供一系列參數來配置線程池,通過不同的參數創建不同的線程池.

Android中的線程池主要分爲4類,可以通過Executer的工廠方法得到.由於Android中的線程池都是直接或者間接通過配置ThreadPoolExecutor來實現,所以首先我們先介紹一下ThreadPoolExecutor.

ThreadPoolExecutor的參數

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

corePoolSize

線程池的核心線程數.默認情況下,核心線程會在線程池中一直存活,即便它們處於閒置狀態.但如果將ThreadPoolExecutorallowCoreThreadTimeOut屬性設置爲true,那麼閒置的核心線程在等待任務到來時會有超時機制,這個時間間隔由keepAliveTime 指定.當等待時間超過指定時長,核心線程也會被終止.

maximumPoolSize

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

keepAliveTime

非核心線程閒置時的超時時長,超過時長,非核心線程會被回收.當ThreadPoolExecutorallowCoreThreadTimeOut屬性設置爲true時,keepAliveTime也會做用於核心線程.

unit

用於指定keepAliveTime參數的時間單位,是一個枚舉,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分鐘)等.

workQueue

線程池中的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個參數中.

threadFactory

線程工廠,爲線程池提供創建新線程的功能.是一個接口,只有一個方法Thread new Thread(Ruunable runnable).

RejectedExecutionHandler

飽和策略,當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採取一種策略處理提交的新任務,默認值是AbortPolicy,表示無法處理新任務時拋出異常.會拋出RejectedExecutionException來通知調用者.
ThreadPoolExecutorRejectedExecutionHandler提供了介個可選值:

  • CallerRunPolicy : 只用調用者所在的線程來執行任務.
  • DiscardPolicy : 不處理,丟棄掉.
  • DiscardOldestPolicy : 丟棄隊列裏最近的一個任務並執行當前任務

ThreadPoolExecutor執行任務時的規則:

  • 有新任務時,如果線程池中的線程數量未達到核心線程的數量,直接啓動一個核心線程.
  • 如果線程中的線程數量已經達到或者超過核心線程數量,那麼任務會被插入任務隊列排隊等待執行.
  • 如果無法將任務插入任務隊列時,往往可能是由於任務隊列已滿,這時如果線程數量未到達線程池規定的最大值,那麼會立刻啓動一個非核心線程來執行任務.如果線程數量已經達到線程池規定的最大值,那麼就拒絕執行該任務,同時ThreadPoolExecutor會調用RejectedExecutionHandlerrejectedExecution方法來通知調用者.

ThreadPoolExecutor的配置

AsynTask中也使用了線程池.我們來看一看是如何對線程池進行配置的.

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    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;
    
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

分析:

核心線程數:最多4個最少2個;
線程池最大線程數:CPU核心數的2倍+1;
核心線程設置超時機制爲true,超時時間與非核心線程在閒置時的超時時間都爲30秒;
任務隊列的容量:128;

線程池的分類

將常見的線程池根據不同的功能特性分如下幾類:

  • FixedThreadPool
  • CachedThreadPool
  • ScheduledThreadPool
  • SingleThreadExecutor

FixedThreadPool

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

通過Executor.newFixedThreadPool方法創建.
是一種線程數量固定的線程池,當線程處於空閒狀態時,也不會被回收,除非線程池關閉.當所有線程都處於活動狀態時,新任務都會處於等待狀態,直到線程空閒出來.由於FixThreadPool只有核心線程並且不會被回收,所以它能夠更快的響應外界的請求.

CachedThreadPool

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

通過Executor.newCachedThreadPool方法創建.是一種線程數量不定的線程池,並且最大線程數爲Integer.MAX_VALUE(0x7fffffff),所以實際上最大線程數可以任意大.當線程池中的線程都處於活動狀態時,線程池會創建新的線程來處理新任務,否則就會利用空閒的線程來處理新任務.線程池中的空閒線程都有超時機制,這個超時時長爲60秒,超過60秒閒置線程就會被回收.和FixThreadPool不同的是,CachedThreadPool的任務隊列其實相當於一個空集合,這樣任何的任務都會立即被執行,因爲在這種場景下SynchronousQueue是無法插入任務的,SynchronousQueue是一個非常特殊的隊列,在很多情況下可以把它簡單理解爲一個無法存儲元素的隊列.
CachedThreadPool的特性來看,這類線程池比較審覈執行大量的耗時較少的任務,當整個線程池都處於閒置狀態時,線程池中的線程都會超時而被停止,此時相當於沒有任何線程,幾乎不佔用任何系統資源.

ScheduledThreadPool

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

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

通過Executor.newSingleThreadExecutor方法創建.它的核心線程數量是固定的,非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收.主要用於執行定時任務和具有固定週期的重複任務.

SingleThreadExecutor

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

通過Executor.newSingleThreadExecutor方法創建.這類線程池內部只有一個核心線程,它確保所有的任務都在同一個線程中按順序執行.SingleThreadExecutor

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章