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

Android中的線程池(二)

編輯:關於Android編程

上一篇博客Android中的線程池(一),簡單分析了線程池的內部工作的過程,有興趣的同學可以去閱讀下。那真的是簡單分析,因為在那篇文章裡,只從一個任務從提交到被執行的過程簡單分析。事實上線程池的內部實現原理是挺復雜的。

這篇博客介紹各種線程池。等我哪一天真正完全理解了線程池的內部每一個細節的實現,會寫一篇完整的源碼分析文章。

線程池說白了就是用於管理線程的。他的作用總結如下:

1. 復用線程池中的線程,避免了大量的線程創建和銷毀帶來的性能開銷

2. 有效控制線程池的最大並發線程數,避免大量的線程之間因為搶占競爭系統的資源而阻塞

3. 提供定時執行,並發數控制等功能

線程池都實現了ExecutorService接口,該接口的實現類有ThreadPoolExecutor,ScheduledThreadPoolExecutor。線程池中有線程,並且封裝了任務隊列。

從api開始分析。

ThreadPoolExecutor

ThreadPoolExecutor的構造方法如下:

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

corePoolSize:核心線程數

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

maximumPoolSize:線程池中所能容納的最大線程數

如果任務隊列已經滿了,無法插入了,此時如果線程數量還沒有達到線程池的最大值,那麼會啟動一個非核心線程來執行任務。

keepAliveTime:超時時長

非核心線程處於閒置狀態如果超過了這個時長,就會被回收,當然核心線程也是可以被回收的(核心線程默認會在線程池中一直存活)。如果我們將allowCoreThreadTimeOut屬性設為true的話,核心線程處於閒置狀態超過了keepAliveTime設定的時長,也會被回收。可以看下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;

unit:超時時長keepAliveTime的單位

workQueue:任務隊列

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

threadFactory:線程工廠

通常不需要用到它

Handler:拒絕策略

如果線程數量已經達到線程池規定的最大值了,那麼就拒絕執行此任務。

好了,詳細介紹了各個參數。我們先總結一下線程池:

提交給線程池的任務,會優先被核心線程執行,如果核心線程全部都在運行狀態,無閒置核心線程了,那麼新的任務會進入任務隊列排隊等待,遵循先進先出的原則。當任務隊列也已經滿了,但是還有新的任務進來,那麼分兩種情況

1. 如果線程池還沒滿(還沒達到最大線程數量,還能裝入線程),此時會啟動非核心線程來執行任務,非核心線程執行完任務,處於閒置狀態後,遵循超時策略,超出時長會被回收。所以不必擔心線程數量一直在積壓

2. 如果線程池已經滿了,裝不下了,那麼會有拒絕策略,拒絕新的任務進來

繼續前進。

通過配置ThreadPoolExecutor的構造方法的參數,可以實現很多種線程池。我們的前輩們已經幫我們分好類了。上一篇也提過,線程池通過工廠方法來創建,我們最常用到的線程池是FixedThreadPool,那就先介紹他了。

1.FixedThreadPool

只需要簡單的一行代碼就能完成FixedThreadPool的創建工作

ExecutorService executorService = Executors.newFixedThreadPool(6);

我們就這樣創建了容納恆定6個線程的線程池

newFixedThreadPool方法如下:

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

其實就是內部調用ThreadPoolExecutor的構造方法嘛,然後配置一些參數,所以說ThreadPoolExecutor很核心。

可以看到核心線程數和最大線程數都是nThreads,超時時長為0毫秒,任務隊列為無界任務隊列。

可以先看看無界的任務隊列LinkedBlockingQueue

public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}
/**
 * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
 *
 * @param capacity the capacity of this queue
 * @throws IllegalArgumentException if {@code capacity} is not greater
 *         than zero
 */
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node(null);
}

這種任務隊列基於鏈表結構,大小是沒有限制的。也就是說根本不會滿

所以我們可以得出結論,FixedThreadPool中只有固定數量的核心線程,並且沒有超時機制,所以即使是閒置也不會被回收,而且任務隊列大小沒有限制,可以一直排著長隊。

2.CachedThreadPool

同樣只需要一行代碼就能完成CachedThreadPool的創建

ExecutorService executorService = Executors.newCachedThreadPool();

這是一個很神奇的線程池,同樣看到其內部ThreadPoolExecutor的參數配置,就能了解這種線程池的功能了

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

他沒有核心線程數的概念,也就是說他只有非核心線程。並且最大線程數是非常大的數,可以認為線程數量不定,可以任意大。幸好還有超時時長,超過了60秒的閒置線程會被回收。。不然我同時有100個任務,那就要創建100個線程了並且 還在不斷的積累數量。當然這種線程如果我一個任務都沒有,那麼線程池中就可以一個線程都沒有,全部都被回收了。為什麼說這個線程池很神奇,因為他的任務隊列是不能存儲元素的。這樣也說明了,只要有任務到達,就會立即被執行。不會放到任務隊列排隊。我們分析,如果任務隊列不能存儲新任務了,那麼這種線程是適合額執行大量的耗時較少的任務。是一種用空間來換時間的做法,占用的內存大,它的並發數量越大,也就是任務越快被執行

3.SingleThreadExecutor

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

從配置可以看出,這個線程池中只有一個線程,並且是核心線程。如果很多任務,全部放在任務隊列排隊,然後一直在復用這個線程執行任務

至於ThreadPoolExecutor執行就很簡單了,一行代碼,execute方法中傳進任務即可。command是Runnable類型的參數。

executorService.execute(command);

當然也可以像上一篇博客寫的那樣用submit方法,submit是可以返回future對象,future可以對任務的執行結果進行獲取等。這個看需求。

可以嘗試提交100個任務進入線程池,每個任務休眠2秒模擬一下。這樣就能感受線程池了。

ScheduledThreadPoolExecutor

從名字上看,這種線程池是可以用於執行定時任務的。我們可以這樣使用:

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(6);

//定時執行,1000ms後執行command任務
scheduledExecutorService.schedule(command,1000, TimeUnit.MILLISECONDS);

//周期重復執行,先延時10ms,然後每隔1000ms執行一次command任務
scheduledExecutorService.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);

newScheduledThreadPool方法如下:

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

跟進ScheduledThreadPoolExecutor

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

核心線程數固定,但是可以容納任意多個線程。同樣的有超時時長

private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;

關於scheduleAtFixedRate方法,參數一是傳入執行的任務,參數二是第一次運行任務時的延遲時間,參數三是定時任務的周期,參數四是時間單位

好了。以上就是Android中常用到的線程池。那麼期待下一篇AsyncTask的實現原理

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