Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 總結:Android中的線程,線程池相關(二)---線程池

總結:Android中的線程,線程池相關(二)---線程池

編輯:關於Android編程

前言

在操作系統中,線程是操作系統調度的最小單元,同事線程又是一種受限的系統資源,即線程不能無限制地產生,並且線程的創建和銷毀都有一定的開銷.當系統中存在大量的線程時,系統會通過時間輪轉片的方式調度每一個線程,因此線程不可能做到絕對的秉性,除非線程數量小於等於CPU的核心數,一般來說這是不可能的.試想一下,如果在一個線程中頻繁的創建和銷毀線程,這顯然不是高效的做法.正確的做法是——采用線程池.線程池中會緩存一定數量的線程,通過線程池就可以避免因為頻繁創建和銷毀線程所帶來的系統開銷.Android中的線程池來源於Java,主要是通過Excutor來派生特定類型的線程池,不同種類的線程池又具有各自的特性.
本篇博客基於java.util.concurrent包,記錄一下本人對線程池的理解,水平有限,難免出錯!


一.Java Executor框架

上圖是Executor框架的結構圖,對於目前階段的我來說,想弄清楚全部的類和結構以及設計思想,是一件十分困難的事情.但是搞清楚部分常用的模塊還是可以達到的,下面就簡單記錄一下此框架中常用的幾個部分.


1.1 Interface Executor

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

Executor接口是Executor框架中最基礎的部分了,定義了一個用於執行Runnable的execute()方法.它沒有直接的實現類,但是有一個重要的子接口ExecutorService.


1.2 Interface ExecutorService

//繼承自Executor接口
  public interface ExecutorService extends Executor {
      /**
       * 關閉方法,調用後執行之前提交的任務,不再接受新的任務
       */
     void shutdown();
     /**
       * 從語義上可以看出是立即停止的意思,將暫停所有等待處理的任務並返回這些任務的列表
      */
     List shutdownNow();
     /**
      * 判斷執行器是否已經關閉
      */
     boolean isShutdown();
     /**
      * 關閉後所有任務是否都已完成
      */
     boolean isTerminated();
     /**
      * 中斷
      */
     boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
     /**
      * 提交一個Callable任務
      */
      Future submit(Callable task);
     /**
      * 提交一個Runable任務,result要返回的結果
      */
      Future submit(Runnable task, T result);
     /**
      * 提交一個任務
     */
     Future submit(Runnable task);
     /**
      * 執行所有給定的任務,當所有任務完成,返回保持任務狀態和結果的Future列表
     */
      List> invokeAll(Collection> tasks)
        throws InterruptedException;
     /**
     * 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。
      */
      List> invokeAll(Collection> tasks,
                                   long timeout, TimeUnit unit)
         throws InterruptedException;
     /**
      * 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果。
      */
      T invokeAny(Collection> tasks)
         throws InterruptedException, ExecutionException;
     /**
      * 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。
      */
      T invokeAny(Collection> tasks,
                     long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
 }
ExecutorService接口繼承自Executor接口,並對execute()方法進行了擴展,定義了終止任務,提交任務,跟蹤任務,返回結果等方法. ExecutorService類涉及到的Runnable接口,callable接口,Future接口的內容如下.
public interface Runnable {
    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}
public interface Callable {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
// Future代表異步任務的執行結果
  public interface Future {

      /**
       * 嘗試取消一個任務,如果這個任務不能被取消(通常是因為已經執行完了),返回false,否則返回true。
       */
      boolean cancel(boolean mayInterruptIfRunning);

      /**
      * 返回代表的任務是否在完成之前被取消了
      */
     boolean isCancelled();

     /**
      * 如果任務已經完成,返回true
      */
     boolean isDone();

     /**
      * 獲取異步任務的執行結果(如果任務沒執行完將等待)
      */
     V get() throws InterruptedException, ExecutionException;

     /**
      * 獲取異步任務的執行結果(有最常等待時間的限制)
      *
      *  timeout表示等待的時間,unit是它時間單位
      */
     V get(long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
 }

1.3 ThreadPoolExecutor

此類的結構如下

java.util.concurrent 
類 ThreadPoolExecutor

java.lang.Object
  繼承者 java.util.concurrent.AbstractExecutorService
      繼承者 java.util.concurrent.ThreadPoolExecutor

在ThreadPoolExecutor和ExecutorService接口之間還有一個實現接口的抽象類AbstractExecutorService,這裡就不做過多記錄,詳情可以參考API文檔

ThreadPoolExecutor是線程池真正的實現類,他的構造方法實現了一系列參數來配置線程池,下面記錄ThreadPoolExecutor的構造方法中每一個參數的含義,這些參數將會直接影響到線程池的功能特性.
下面是ThreadPoolExecutor的常用構造方法:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BolckingQueue workQueue,
                          ThreadFactory threadFactory)
corePoolSize

線程的核心線程數,默認情況下,核心線程會在線程池中一直存活,及時他們處於閒置狀態.如果將ThreadPoolExecutor的allowCoreThreadTimeout屬性設置為true,那麼閒置的核心線程在等待新任務到來的時候會有超時策略,這個時間由keepAliveTime所指定,等待時間超過其所指定的時間後,核心線程就會被終止.

maximumPoolSize

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

keepAliveTime

非核心線程閒置時的超時時長,超過這個時長,非核心線程就會被回收.當ThreadPoolExecutor的allowCoreThreadTimeout屬性設置為true時,keepAliveTimeout將同樣會作用於核心線程.

unit

用於指定keepAliveTimeout參數的單位時間,這是一個枚舉,常用的有TimeUnit.MILLISECONDS,TimeUnit.SECONDS,TimeUnit.MINUTES.

workQueue

線程池中的任務隊列,通過線程池的execute()方法提交的Runnable對象會存儲在這個參數中.
此處工作隊列常用的子類有三種,分別是:

ArrayBlockingQueue:

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

LinckedBlockingQueue:

一個基於已鏈接節點的、范圍任意的 blocking queue。此隊列按 FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部 是在隊列中時間最短的元素。新元素插入到隊列的尾部,並且隊列獲取操作會獲得位於隊列頭部的元素。鏈接隊列的吞吐量通常要高於基於數組的隊列,但是在大多數並發應用程序中,其可預知的性能要低。

可選的容量范圍構造方法參數作為防止隊列過度擴展的一種方法。如果未指定容量,則它等於 Integer.MAX_VALUE。除非插入節點會使隊列超出容量,否則每次插入後會動態地創建鏈接節點。

SynchronousQueue:

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

threadFactory

線程工廠,為線程池提供創建新線程的功能.ThreadFactory是一個接口,他只有一個方法:Thread newThread(Runnable r).

以上ThreadPoolExecutor的參數配置在AsyncTask中有明顯的體現:

public abstract class 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 sPoolWorkQueue =
            new LinkedBlockingQueue(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

從上面的代碼我們可以知道,AsyncTask對THREAD_POOL_EXECUTOR這個線程池進行了配置,配置後的線程池規格如下:

核心線程數等於CPU核心數+1; 線程池的最大線程數等於CPU核心數的2倍+1; 核心線程無超時機制,非核心線程在閒置時的超時時間為1秒; 任務隊列采用的是容量為128的有界隊列; -

1.4 Executors

Executors中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。此類支持以下各種方法:

創建並返回設置有常用配置字符串的 ExecutorService 的方法。 創建並返回設置有常用配置字符串的 ScheduledExecutorService 的方法。 創建並返回“包裝的”ExecutorService 方法,它通過使特定於實- - 現的方法不可訪問來禁用重新配置。 創建並返回 ThreadFactory 的方法,它可將新創建的線程設置為已知的狀態。

創建並返回非閉包形式的 Callable 的方法,這樣可將其用於需要 Callable 的執行方法中。

Executors提供的都是工具形式的方法,所以都是static的,並且這個類也沒有必要實例化,所以它的構造方法時private的。

下面結合線程池的分類,來記錄Executors類的幾個常用創建線程池的靜態方法.

FixedThreadPool
通過Executors的newFixedThreadPool方法來創建.它是一種線程數量固定的線程池,當線程處於空閒狀態時,他們並不會被回收,除非線程池被關閉了. 當所有的線程都處於活動狀態時,新任務就處於等待狀態,直到有線程空閒出來. FixedThreadPool中只有核心線程,並且沒有超時機制,並且任務隊列沒有大小限制. 由於只有核心線程並且不會被回收,這意味著它能夠更加快速的響應外界的請求.
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }

CachedThreadPool

通過Executors的newCachedThreadPool方法來創建.它是一種線程數量不定的線程池,且只有非核心線程.並且最大線程數為Integer.MAX_VALUE,相當於任意大. 當線程池中的線程都處於活動狀態時,線程池會創建新的線程來處理新任務,否則就會利用空閒的線程來處理新任務. 線程池中的空閒線程都有超時機制,這個超時時長是60s,超過60s閒置線程就會被收回. 和FixedThreadPool不同的是,CacheTHreadPool的任務隊列相當於一個空的集合,這將導致任何任務都會立即被執行,因為在這種場合下SynchronousQueue是無法插入任務的. 當整個線程池都處於閒置狀態時,線程池中的線程都會超時而被停止,這個時候CacheThreadPool之中實際上是沒有任何線程的,它幾乎不占任何系統資源. 這類線程池比較適合執行大量的耗時較少的任務.
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

ScheduledThreadPool

通過Executors的newScheduledThreadPool方法來創建. 它的核心線程數是固定的,非核心線程數是沒有限制的,並且當非核心線程閒置時,會立即被回收. 這類線程池主要用於執行定時任務和具有固定周期的重復任務.
注:此類的構造方法由於版本的不同,可能會存在一定的差異
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());
    }
SingleThreadPool
通過Executors的newSingleThreadPool方法創建. 該線程池的內部有且只有一個核心線程.,沒有超時時間 確保所有的任務都在同一個線程中按順序執行. 此類的意義在於統一所有的外界任務到一個線程中,這使得在這些任務之間不需要處理線程同步的問題.
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }

以上就是Android中常見的四種線程池,出了上面系統提供的四種線程以外,我們還可以根據實際的需要,靈活的配置線程池.

二.使用線程池的優點及規則

2.1 優點

重用線程池中的線程,避免因為線程的創建和銷毀帶來的系統性能開銷.

能有效地控制線程池最大並發數,避免大量的線程之間因為相互搶占資源而導致的阻塞現象.

能夠對線程進行簡單的管理,並提供定時執行以及指定間隔循環執行等功能.

2.2規則

如果線程池中的線程數量未達到核心線程數,那麼會直接啟動一個核心線程來執行任務.

如果線程池中的線程數量已經達到或超過核心線程數,那麼任務會被插入到任務隊列中排隊等待執行.

如果在步驟2中無法插入到任務隊列中,這往往是由於隊列已滿,這個時候如果線程數未達到線程池規定的最大值,那麼會立刻啟動一個非核心線程類執行任務

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

 

 

 

 

下一篇結合Callable,Future,FutureTask記錄下線程池的具體使用.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved