【后台任务】为多个线程创建一个管理器(5)

概要


前面的指南教你如何指定要在线程上运行的代码,它显示了如何定义在单独的线程上执行的任务。如果你只想运行一次任务,这可能就是你所需要的。如果您想在不同的数据集上重复执行任务,但您只需要一次执行一次执行,就能IntentService满足您的需求。要在资源可用时自动运行任务,或者允许多个任务同时运行(或同时运行),则需要提供受管理的线程集合。为此,请使用一个实例ThreadPoolExecutor,当其池中的线程变为空闲时,该实例 将从队列中运行任务。要运行任务,您只需将其添加到队列中即可。

一个线程池可以运行一个任务的多个并行实例,所以你应该确保你的代码是线程安全的。包含一个synchronized块中可以被多个线程访问的变量 。这种方法将阻止一个线程读取变量,而另一个线程正在写入该变量。通常情况下,这种情况出现在静态变量中,但它也出现在仅实例化一次的任何对象中。要了解更多信息,请阅读 进程和线程概述指南。

定义线程池类


ThreadPoolExecutor在自己的类中 实例化。在本课程中,请执行以下操作:

使用线程池的静态变量
您可能只需要为应用程序提供一个线程池实例,以便为受限制的CPU或网络资源提供单个控制点。如果你有不同的 Runnable类型,你可能想要为每个类型创建一个线程池,但其中的每一个都可以是单个实例。例如,您可以将其添加为全局字段声明的一部分:

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

使用私有构造函数
使构造函数保持私有性可确保它是单例,这意味着您不必在synchronized块中包含对类的访问:

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线程中
A Handler允许您的应用安全地调用诸如对象之类的UI对象的方法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();
}

该数字可能不会反映设备中物理核心的数量; 一些设备具有取消激活一个或多个内核的CPU,具体取决于系统负载。对于这些设备,availableProcessors()返回活动核心的数量, 该数量 可能小于核心总数。

保持活跃的时间和时间单位
线程在关闭之前保持空闲的持续时间。持续时间由时间单位值解释,该时间单位值是在中定义的常量之一 TimeUnit。

任务队列
从中ThreadPoolExecutor接收 Runnable对象的传入队列。为了在线程上启动代码,线程池管理器Runnable从先进先出队列中获取 对象并将其附加到线程。您在创建线程池时使用任何实现该BlockingQueue接口的队列类提供此队列对象。为了匹配您的应用程序的要求,您可以从可用的队列实现中进行选择; 要了解更多关于它们的信息,请参阅课程概述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);
    }

更多信息


要了解更多关于Android上的多线程操作的信息,请参阅过程和线程概述指南。

示例应用


要尝试本指南中的概念,请下载ThreadSample

Lastest Update:2018.04.17

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

【后台任务】为多个线程创建一个管理器(5)

猜你喜欢

转载自blog.51cto.com/4789781/2124453