在 Android 中使用 ThreadPoolExecutor

「阿里巴巴 Android 开发手册」中第 5.3 节对新建线程做了如下强制性要求:

新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。

并对此做了如下的说明:

使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。另外创建匿名线程不便于后续的资源使用分析,对性能分析等会造成困扰。

嗯,有理有据,让人信服。接下来将介绍 Thread Pools, Thread Pool Executors 以及它们在 Android 中的使用。

先欣赏一张美丽的图片:

Thread Pool Executor

Thread Pool

一个线程池(Thread Pool)管理着一些工作线程(work threads)(具体的数字取决于它的实现方式)。

任务队列(Task Queue)中存储着一些等待着被线程池中的空闲线程执行的任务,这些任务被生产者(producers)添加到队列中,线程池中的工作线程(work threads)充当消费者(consumers)。一旦线程池中有空闲线程准备好执行新的后台任务,它们便会执行(consuming)队列中的任务。

ThreadPoolExecutor

ThreadPoolExecutor 使用线程池中的一个线程去执行给定的 task。

1
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {}

上面是 ThreadPoolExecutor 构造函数中的几个重要参数, 这些参数有什么含义?

  1. corePoolSize:保留在线程池中的最小线程数,最初在线程池中只有 0 个线程,随着任务被添加到队列中,新线程便会被创建。规则:假设线程池中存在空闲的线程,但是池中线程数小于 corePoolSize,那么将会继续创建新的线程。
  2. maximumPoolSize:线程池中允许存在的最大线程数,如果 maximumPoolSize 大于 corePoolSize,并且当前池中的线程数大于等于 corePoolSize,那么只有当任务队列已满时才会创建新的线程。
  3. keepAliveTime:当池中线程的数量大于 core 数时,多余的空闲线程如果在该参数指定的时间内没有等待到一个新的任务,那么它们将会死掉。
  4. unit:参数 keepAliveTime 的单位。
  5. workQueue:上面提到的任务队列(task queue),它必须是一个 BlockingQueue,并且只能存储 runnable tasks。

为什么要在 Android 中使用 ThreadPoolExecutor

  • ThreadPoolExecutor 是一个强大的任务执行框架,它支持任务添加、取消任务、给任务声明优先级等操作。
  • ThreadPoolExecutor 减少了与线程创建相关的开销,因为它在其线程池中管理了一定数量的线程。

在 Android 中使用 ThreadPoolExecutor

首先创建 PriorityThreadFactory(将作为 ThreadPoolExecutor 的一个参数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PriorityThreadFactory implements ThreadFactory {
private final int mThreadPriority;
public PriorityThreadFactory(int threadPriority) {
mThreadPriority = threadPriority;
}
@Override public Thread newThread(@NonNull final Runnable runnable) {
Runnable wrapperRunnable = new Runnable() {
@Override public void run() {
try {
Process.setThreadPriority(mThreadPriority);
} catch (Throwable t) {
}
runnable.run();
}
};
return new Thread(wrapperRunnable);
}
}

创建 MainThreadExecutor(用于执行主线程中的 tasks):

1
2
3
4
5
6
7
8
public class MainThreadExecutor implements Executor {
private Handler mHandler = new Handler(Looper.getMainLooper());
@Override public void execute(@NonNull Runnable runnable) {
mHandler.post(runnable);
}
}

创建 DefaultExecutorSupplier(构建、管理不同的 ThreadPoolExecutor )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class DefaultExecutorSupplier {
// 决定池中线程的数量
private static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
// 用于后台任务的 ThreadPoolExecutor
private final ThreadPoolExecutor mForBackgroundTasks;
// 用于轻量级后台任务的 ThreadPoolExecutor
private final ThreadPoolExecutor mForLightWeightBackgroundTasks;
// 用于执行主线程中的 tasks
private final MainThreadExecutor mMainThreadExecutor;
// DefaultExecutorSupplier 的一个实例
private static DefaultExecutorSupplier instance;
// 利用单列模式返回 DefaultExecutorSupplier 的实例
public static DefaultExecutorSupplier getInstance() {
if (instance == null) {
synchronized (DefaultExecutorSupplier.class) {
if (instance == null) {
instance = new DefaultExecutorSupplier();
}
}
}
return instance;
}
// DefaultExecutorSupplier 的私有构造函数
private DefaultExecutorSupplier() {
// 设置 thread factory
ThreadFactory priorityThreadFactory = new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND);
// 构建用于后台任务的 mForBackgroundTasks
mForBackgroundTasks = new ThreadPoolExecutor(NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
priorityThreadFactory);
// 构建用于轻量级后台任务的 mForLightWeightBackgroundTasks
mForLightWeightBackgroundTasks = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
priorityThreadFactory);
// 构建 mMainThreadExecutor
mMainThreadExecutor = new MainThreadExecutor();
}
public ThreadPoolExecutor forBackgroundTasks() {
return mForBackgroundTasks;
}
public ThreadPoolExecutor forLightWeightBackgroundTasks() {
return mForLightWeightBackgroundTasks;
}
public Executor forMainThreadTasks() {
return mMainThreadExecutor;
}
}

PS:corePoolSize 和 maximumPoolSize 请根据自己的时间需求进行设置。

现在,在你的代码中使用它吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 将它用于后台任务
public void doSomeBackgroundWork(){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some background work here.
}
});
}
// 将它用于轻量级后台任务
public void doSomeLightWeightBackgroundWork(){
DefaultExecutorSupplier.getInstance().forLightWeightBackgroundTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some light-weight background work here.
}
});
}
// 将它用于主线程的任务
public void doSomeMainThreadWork(){
DefaultExecutorSupplier.getInstance().forMainThreadTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some Main Thread work here.
}
});
}

通过这种方式,我们可以为网络任务, I/O 操作,耗时的后台任务等创建不同的线程池。enjoy it.

如何取消一个任务

为了取消一个任务,应该使用 submit 方法而不是 execute,它将返回一个 future 对象,使用该返回值可以取消任务。

1
2
3
4
5
6
7
8
9
10
11
// 通过将任务 submitting 到线程池中来得到 future
Future future = DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.submit(new Runnable() {
@Override
public void run() {
// do some background work here.
}
});
// 取消这个 task
future.cancel(true);

全文完

感谢阅读