Android 為多線程創(chuàng)建管理器

2018-08-02 18:27 更新

編寫:AllenZheng1991 - 原文:http://developer.android.com/training/multiple-threads/create-threadpool.html

在前面的課程中展示了如何在單獨(dú)的一個(gè)線程中執(zhí)行一個(gè)任務(wù)。如果你的線程只想執(zhí)行一次,那么上一課的內(nèi)容已經(jīng)能滿足你的需要了。

如果你想在一個(gè)數(shù)據(jù)集中重復(fù)執(zhí)行一個(gè)任務(wù),而且你只需要一個(gè)執(zhí)行運(yùn)行一次。這時(shí),使用一個(gè)IntentService將能滿足你的需求。 為了在資源可用的的時(shí)候自動(dòng)執(zhí)行任務(wù),或者允許不同的任務(wù)同時(shí)執(zhí)行(或前后兩者),你需要提供一個(gè)管理線程的集合。 為了做這個(gè)管理線程的集合,使用一個(gè)ThreadPoolExecutor實(shí)例,當(dāng)一個(gè)線程在它的線程池中變得不受約束時(shí),它會(huì)運(yùn)行隊(duì)列中的一個(gè)任務(wù)。 為了能執(zhí)行這個(gè)任務(wù),你所需要做的就是把它加入到這個(gè)隊(duì)列。

一個(gè)線程池能運(yùn)行多個(gè)并行的任務(wù)實(shí)例,因此你要能保證你的代碼是線程安全的,從而你需要給會(huì)被多個(gè)線程訪問的變量附上同步代碼塊(synchronized block)。 當(dāng)一個(gè)線程在對(duì)一個(gè)變量進(jìn)行寫操作時(shí),通過這個(gè)方法將能阻止另一個(gè)線程對(duì)該變量進(jìn)行讀取操作。 典型的,這種情況會(huì)發(fā)生在靜態(tài)變量上,但同樣它也能突然發(fā)生在任意一個(gè)只實(shí)例化一次。 為了學(xué)到更多的相關(guān)知識(shí),你可以閱讀進(jìn)程與線程這一API指南。

定義線程池類

在自己的類中實(shí)例化ThreadPoolExecutor類。在這個(gè)類里需要做以下事:

1. 為線程池使用靜態(tài)變量

為了有一個(gè)單一控制點(diǎn)用來限制CPU或涉及網(wǎng)絡(luò)資源的Runnable類型,你可能需要有一個(gè)能管理所有線程的線程池,且每個(gè)線程都會(huì)是單個(gè)實(shí)例。比如,你可以把這個(gè)作為一部分添加到你的全局變量的聲明中去:

public class PhotoManager {
    ...
    static  {
        ...
        // Creates a single static instance of PhotoManager
        sInstance = new PhotoManager();
    }
    ...

2. 使用私有構(gòu)造方法

讓構(gòu)造方法私有從而保證這是一個(gè)單例,這意味著你不需要在同步代碼塊(synchronized block)中額外訪問這個(gè)類:

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() {
    ...
    }

3.通過調(diào)用線程池類里的方法開啟你的任務(wù)

在線程池類中定義一個(gè)能添加任務(wù)到線程池隊(duì)列的方法。例如:

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());
        ...
    }

4. 在構(gòu)造方法中實(shí)例化一個(gè)Handler,且將它附加到你APP的UI線程。

一個(gè)Handler允許你的APP安全地調(diào)用UI對(duì)象(例如 View對(duì)象)的方法。大多數(shù)UI對(duì)象只能從UI線程安全的代碼中被修改。這個(gè)方法將會(huì)在與UI線程進(jìn)行通信(Communicate with the UI Thread)這一課中進(jìn)行詳細(xì)的描述。例如:

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) {
                ...
            }
        ...
        }
    }

確定線程池的參數(shù)

一旦有了整體的類結(jié)構(gòu),你可以開始定義線程池了。為了初始化一個(gè)ThreadPoolExecutor對(duì)象,你需要提供以下數(shù)值:

1. 線程池的初始化大小和最大的大小

這個(gè)是指最初分配給線程池的線程數(shù)量,以及線程池中允許的最大線程數(shù)量。在線程池中擁有的線程數(shù)量主要取決于你的設(shè)備的CPU內(nèi)核數(shù)。

這個(gè)數(shù)字可以從系統(tǒng)環(huán)境中獲得:

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();
}

這個(gè)數(shù)字可能并不反映設(shè)備的物理核心數(shù)量,因?yàn)橐恍┰O(shè)備根據(jù)系統(tǒng)負(fù)載關(guān)閉了一個(gè)或多個(gè)CPU內(nèi)核,對(duì)于這樣的設(shè)備,availableProcessors()方法返回的是處于活動(dòng)狀態(tài)的內(nèi)核數(shù)量,可能少于設(shè)備的實(shí)際內(nèi)核總數(shù)。

2.線程保持活動(dòng)狀態(tài)的持續(xù)時(shí)間和時(shí)間單位

這個(gè)是指線程被關(guān)閉前保持空閑狀態(tài)的持續(xù)時(shí)間。這個(gè)持續(xù)時(shí)間通過時(shí)間單位值進(jìn)行解譯,是TimeUnit()中定義的常量之一。

3.一個(gè)任務(wù)隊(duì)列

這個(gè)傳入的隊(duì)列由ThreadPoolExecutor獲取的Runnable對(duì)象組成。為了執(zhí)行一個(gè)線程中的代碼,一個(gè)線程池管理者從先進(jìn)先出的隊(duì)列中取出一個(gè)Runnable對(duì)象且把它附加到一個(gè)線程。當(dāng)你創(chuàng)建線程池時(shí)需要提供一個(gè)隊(duì)列對(duì)象,這個(gè)隊(duì)列對(duì)象類必須實(shí)現(xiàn)BlockingQueue接口。為了滿足你的APP的需求,你可以選擇一個(gè)Android SDK中已經(jīng)存在的隊(duì)列實(shí)現(xiàn)類。為了學(xué)習(xí)更多相關(guān)的知識(shí),你可以看一下ThreadPoolExecutor類的概述。下面是一個(gè)使用LinkedBlockingQueue實(shí)現(xiàn)的例子:

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>();
        ...
    }
    ...
}

創(chuàng)建一個(gè)線程池

為了創(chuàng)建一個(gè)線程池,可以通過調(diào)用ThreadPoolExecutor()構(gòu)造方法初始化一個(gè)線程池管理者對(duì)象,這樣就能創(chuàng)建和管理一組可約束的線程了。如果線程池的初始化大小和最大大小相同,ThreadPoolExecutor在實(shí)例化的時(shí)候就會(huì)創(chuàng)建所有的線程對(duì)象。例如:

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);
    }


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)