概要
前面的指南教你如何指定要在线程上运行的代码,它显示了如何定义在单独的线程上执行的任务。如果你只想运行一次任务,这可能就是你所需要的。如果您想在不同的数据集上重复执行任务,但您只需要一次执行一次执行,就能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
公众号推荐:
转载于:https://blog.51cto.com/4789781/2124453