原文地址:http://android.xsoftlab.net/training/multiple-threads/create-threadpool.html
上節課我們學習瞭如何定義一個任務。如果只是執行單次任務,那麼剛剛所學的已經基本滿足要求了。如果需要針對不同的數據執行同種任務,並且需要同一時間只能執行一項任務,那麼IntentService可能會適合你。如果要使任務隨着資源的可用而執行,或者同一時間需要運行多個任務,那麼就需要專門管理這些線程了。Android系統爲此提供了一個類,那就是傳說中的ThreadPoolExecutor。它可以在線程可用時自動運行隊列中的任務。如果要運行一個任務,只需要將任務添加到隊列中即可。
ThreadPoolExecutor允許多個線程同時進行,所以應當確保代碼是線程安全的。要確保可能會被多個線程訪問到的變量被放在同步代碼塊中。這種方法可以防止一個線程正在讀取一個變量的值,而另一個變量正在對這個變量寫入新值的現象出現。通常情況下,這種現象會發生在靜態變量中,但是也會發生在單例對象上。
定義線程池類
在該類內實例化一個ThreadPoolExecutor對象,並在該類內執行以下工作:
對線程池對象使用靜態變量引用
在APP內通常只需要一個線程池對象的存在:爲了對CPU資源或網絡資源有一個單一的控制。如果含有不同的Runnable類型,你可能希望對每種類型都創建一個單獨線程池,但是每種類型都還是單例類型。在這裏的例子中,可以添加以下代碼作爲全局屬性聲明。
public class PhotoManager { ... static { ... // Creates a single static instance of PhotoManager sInstance = new PhotoManager(); } ...
使用私有構造方法:
私有的構造方法可以確保該類只有一個對象存在。
public class PhotoManager {
...
/**
* Constructs the work queues and thread pools used to download
* and decode images. Because the constructor is marked private,
* it's unavailable to other classes, even in the same package.
*/
private PhotoManager() {
...
}
調用線程池的相關方法啓動任務:
在線程池類內定義一個可以向線程池隊列中添加任務的方法。public class PhotoManager { ... // Called by the PhotoView to get a photo static public PhotoTask startDownload( PhotoView imageView, boolean cacheFlag) { ... // Adds a download task to the thread pool for execution sInstance. mDownloadThreadPool. execute(downloadTask.getHTTPDownloadRunnable()); ... }
在構造方法中實例化一個Handler對象,並將其連接到UI線程:Handler允許安全的調用比如View這種UI對象。大多數的UI對象只允許被UI線程訪問。
private PhotoManager() { ... // Defines a Handler object that's attached to the UI thread mHandler = new Handler(Looper.getMainLooper()) { /* * handleMessage() defines the operations to perform when * the Handler receives a new Message to process. */ @Override public void handleMessage(Message inputMessage) { ... } ... } }
確定線程池參數
如果要實例化ThreadPoolExecutor對象,需要用到以下值:
線程池的初始值以及最大值:
- 線程數量的初始值用於指定池子的初始大小,最大值代表了該線程池所允許開放的最大併發數量。線程池中的線程數量取決於設備的可用核心數。這個值與當前的設備頗有關係:
public class PhotoManager {
...
/*
* Gets the number of available cores
* (not always the same as the maximum number of cores)
*/
private static int NUMBER_OF_CORES =
Runtime.getRuntime().availableProcessors();
}
這個值可能不能夠反映設備的物理核心數量;在一些設備上,核心是否可用取決於系統是否加載。對於這些設備,availableProcessors()返回了當前的活躍核心數量,這個值可能要比真實的總核心數要低。
存活時長及單位
- 線程在被關閉之間的閒置時長。這個時長由時間單元值負責解釋,這些時間單位常量被定義在類TimeUnit中。
任務隊列
- 一個持有Runnable對象的隊列。爲了啓動線程中的執行代碼,線程池管理器採用了先進先出的管理原則。在創建線程池之前需要提供一個這樣的隊列,使用任何實現了BlockingQueue接口的類皆可。爲了匹配到APP的要求,你可以選擇適當的隊列實現;學習更多它們的相關知識,請參見ThreadPoolExecutor的描述文檔。這裏的使用了LinkedBlockingQueue:
public class PhotoManager {
...
private PhotoManager() {
...
// A queue of Runnables
private final BlockingQueue<Runnable> mDecodeWorkQueue;
...
// Instantiates the queue of Runnables as a LinkedBlockingQueue
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
...
}
...
}
線程池創建
創建一個線程池,只需調用ThreadPoolExecutor()實例化一個線程池管理器就好。它會創建並管理一組線程。因爲線程池大小的初始值與最大值是相同的,所以ThreadPoolExecutor()會在初始化的時候創建出所指定的線程數量:
private PhotoManager() {
...
// Sets the amount of time an idle thread waits before terminating
private static final int KEEP_ALIVE_TIME = 1;
// Sets the Time Unit to seconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
// Creates a thread pool manager
mDecodeThreadPool = new ThreadPoolExecutor(
NUMBER_OF_CORES, // Initial pool size
NUMBER_OF_CORES, // Max pool size
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
mDecodeWorkQueue);
}