这篇文章将会覆盖到线程池线程池Executor,以及它们在Android中的使用。通过大量的示例我们将完全覆盖这些主题。

本文是 Using ThreadPoolExecutor in Android 的翻译, 限于个人能力有限如有疑问请查看原文或留言.

线程池

一个线程池管理一池子的工作线程(工作线程的数量依赖于具体的线程池实现)。

一个任务队列容纳等待被线程池中的空闲线程执行的任务。任务被“生产者”添加到队列中,与之对应的工作线程作为消费者的角色消费任务队列中的任务,只要线程池中有空闲的线程等待去执行后台任务。

线程池Executor

线程池executor执行一个线程池中给定的任务。

1
2
3
4
5
6
7
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
);

这些参数都是什么含义?

  1. corePoolSize: 线程池中保留的线程的最小数量。

    初始情况下线程池中的线程数量为0。但是随着任务被加到队列中,新的线程会被创建。如果在线程池中存在空闲的线程—但是线程的数量小于corePoolSize—那么新的线程会被继续创建。

  2. maximumPoolSize: 线程池中所能容纳的最大线程数量。如果它的数量大于corePoolSize—并且当前的线程数量>= corePoolSize—那么新的工作线程会被继续创建只要任务队列中的任务足够多。

  3. keepAliveTime: 当线程的数量大于corePoolSize,非核心的线程(额外的空闲线程)将会等待新的任务,如果它们在这个参数定义的时间内没有等到一个新的任务,这些线程会被终结。

  4. unit: 参数keepAliveTime的单位

  5. workQueue: 任务队列,它只接收runnable任务。它必须是一个BlockingQueue.

为何要使用线程池在Android或Java程序中

  1. 它是一个强大的任务处理框架,因为它支持额外的任务保存在队列中,支持任务取消,以及设置任务的优先级。
  2. 它减小了所需要创建的线程的最大数量,因为它维护了指定数量的线程在它的池子中。

在Android中使用ThreadPoolExecutor

首先,创建一个PriorityThreadFactory:

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
public class PriorityThreadFactory implements ThreadFactory {

private final int mThreadPriority;

public PriorityThreadFactory(int threadPriority) {
mThreadPriority = threadPriority;
}

@Override
public Thread newThread(final Runnable runnable) {
Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
try {
Process.setThreadPriority(mThreadPriority);
} catch (Throwable t) {

}
runnable.run();
}
};
return new Thread(wrapperRunnable);
}

}

PriorityThreadFactory.java hosted with ❤ by GitHub

创建一个MainThreadExecutor:

1
2
3
4
5
6
7
8
9
public class MainThreadExecutor implements Executor {

private final Handler handler = new Handler(Looper.getMainLooper());

@Override
public void execute(Runnable runnable) {
handler.post(runnable);
}
}

MainThreadExecutor.java hosted with ❤ by GitHub

创建一个DefaultExecutorSupplier:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
* Singleton class for default executor supplier
*/

public class DefaultExecutorSupplier{
/*
* Number of cores to decide the number of threads
*/

public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();

/*
* thread pool executor for background tasks
*/

private final ThreadPoolExecutor mForBackgroundTasks;
/*
* thread pool executor for light weight background tasks
*/

private final ThreadPoolExecutor mForLightWeightBackgroundTasks;
/*
* thread pool executor for main thread tasks
*/

private final Executor mMainThreadExecutor;
/*
* an instance of DefaultExecutorSupplier
*/

private static DefaultExecutorSupplier sInstance;

/*
* returns the instance of DefaultExecutorSupplier
*/

public static DefaultExecutorSupplier getInstance() {
if (sInstance == null) {
synchronized(DefaultExecutorSupplier.class){
sInstance = new DefaultExecutorSupplier();
}
return sInstance;
}

/*
* constructor for DefaultExecutorSupplier
*/

private DefaultExecutorSupplier() {

// setting the thread factory
ThreadFactory backgroundPriorityThreadFactory = new
PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND);

// setting the thread pool executor for mForBackgroundTasks;
mForBackgroundTasks = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);

// setting the thread pool executor for mForLightWeightBackgroundTasks;
mForLightWeightBackgroundTasks = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);

// setting the thread pool executor for mMainThreadExecutor;
mMainThreadExecutor = new MainThreadExecutor();
}

/*
* returns the thread pool executor for background task
*/

public ThreadPoolExecutor forBackgroundTasks() {
return mForBackgroundTasks;
}

/*
* returns the thread pool executor for light weight background task
*/

public ThreadPoolExecutor forLightWeightBackgroundTasks() {
return mForLightWeightBackgroundTasks;
}

/*
* returns the thread pool executor for main thread task
*/

public Executor forMainThreadTasks() {
return mMainThreadExecutor;
}
}

DefaultExecutorSupplier.java hosted with ❤ by GitHub

注意:不同的线程池可以维护不同数量的线程,就看你的实际需要了

现在可以像下面这样在你的代码中使用它们:

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
/*
* Using it for Background Tasks
*/

public void doSomeBackgroundWork(){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some background work here.
}
});
}

/*
* Using it for Light-Weight Background Tasks
*/

public void doSomeLightWeightBackgroundWork(){
DefaultExecutorSupplier.getInstance().forLightWeightBackgroundTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some light-weight background work here.
}
});
}

/*
* Using it for MainThread Tasks
*/

public void doSomeMainThreadWork(){
DefaultExecutorSupplier.getInstance().forMainThreadTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some Main Thread work here.
}
});
}

UsingThreadPool.java hosted with ❤ by GitHub

通过这种方式,我们能创建不同的线程池为网络任务,I/O任务,很重的后台任务,以及其它的任务。

如何取消一个任务

要取消一个任务,你需要得到那个任务的future。所以你需要用submit方法而不是execute,它可以返回一 个future对象。你就可以通过这个future对象来取消那个任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* Get the future of the task by submitting it to the pool
*/

Future future = DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.submit(new Runnable() {
@Override
public void run() {
// do some background work here.
}
});

/*
* cancelling the task
*/

future.cancel(true);

CancelTask.java hosted with ❤ by GitHub

如何设置任务的优先级

我们假设在一个队列中有20个任务,但是线程池中只有4个线程。我们依据它们的优先级来执行,因为线程池只能同时执行4个任务。

但是假设我们需要让最后一个进入队列中的任务先执行。我们需要给这个任务设置IMMEDIATE优先级,这样就可以让线程取任务时优先取这个任务去执行(因为它的优先级最高)。

要设置任务的优先级,我们需要创建一个线程池executor。

创建一个优先级枚举:

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
/**
* Priority levels
*/

public enum Priority {
/**
* NOTE: DO NOT CHANGE ORDERING OF THOSE CONSTANTS UNDER ANY CIRCUMSTANCES.
* Doing so will make ordering incorrect.
*/


/**
* Lowest priority level. Used for prefetches of data.
*/

LOW,

/**
* Medium priority level. Used for warming of data that might soon get visible.
*/

MEDIUM,

/**
* Highest priority level. Used for data that are currently visible on screen.
*/

HIGH,

/**
* Highest priority level. Used for data that are required instantly(mainly for emergency).
*/

IMMEDIATE;

}

Priority.java hosted with ❤ by GitHub

创建一个PriorityRunnable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PriorityRunnable implements Runnable {

private final Priority priority;

public PriorityRunnable(Priority priority) {
this.priority = priority;
}

@Override
public void run() {
// nothing to do here.
}

public Priority getPriority() {
return priority;
}

}

PriorityRunnable.java hosted with ❤ by GitHub

创建一个PriorityThreadPoolExecutor,它继承自ThreadPoolExecutor。我们还需要创建一个PriorityFutureTask,它实现了Comparable

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
public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {

public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, ThreadFactory threadFactory)
{

super(corePoolSize, maximumPoolSize, keepAliveTime, unit,new PriorityBlockingQueue<Runnable>(), threadFactory);
}

@Override
public Future<?> submit(Runnable task) {
PriorityFutureTask futureTask = new PriorityFutureTask((PriorityRunnable) task);
execute(futureTask);
return futureTask;
}

private static final class PriorityFutureTask extends FutureTask<PriorityRunnable>
implements Comparable<PriorityFutureTask> {

private final PriorityRunnable priorityRunnable;

public PriorityFutureTask(PriorityRunnable priorityRunnable) {
super(priorityRunnable, null);
this.priorityRunnable = priorityRunnable;
}

/*
* compareTo() method is defined in interface java.lang.Comparable and it is used
* to implement natural sorting on java classes. natural sorting means the the sort
* order which naturally applies on object e.g. lexical order for String, numeric
* order for Integer or Sorting employee by there ID etc. most of the java core
* classes including String and Integer implements CompareTo() method and provide
* natural sorting.
*/

@Override
public int compareTo(PriorityFutureTask other) {
Priority p1 = priorityRunnable.getPriority();
Priority p2 = other.priorityRunnable.getPriority();
return p2.ordinal() - p1.ordinal();
}
}
}

PriorityThreadPoolExecutor.java hosted with ❤ by GitHub

首先,在DefaultExecutorSupplier中,我们需要用PriorityThreadPoolExecutor代替ThreadPoolExecutor像下面这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DefaultExecutorSupplier{

private final PriorityThreadPoolExecutor mForBackgroundTasks;

private DefaultExecutorSupplier() {

mForBackgroundTasks = new PriorityThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
backgroundPriorityThreadFactory
);

}
}

DefaultExecutorSupplierP.java hosted with ❤ by GitHub

下面是一个例子展示了如果给一个任务设置HIGH优先级:

1
2
3
4
5
6
7
8
9
10
11
12
/*
* do some task at high priority
*/

public void doSomeTaskAtHighPriority(){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.submit(new PriorityRunnable(Priority.HIGH) {
@Override
public void run() {
// do some background work here at high priority.
}
});
}

PriorityTask.java hosted with ❤ by GitHub

通过这种方式,一个任务就可以被设置执行地优先级。

上面的实现也适用于任何JAVA程序。

我用了这个线程池实现在Android Networking Library.

想要了解更多实现细节,你可以把 Android Networking here项目中的DefaultExecutorSupplier.java检出看看。

我希望这些讲解能够帮到你。

谢谢阅读这个文章。如果你喜欢这篇文章,那就在下面点一下❤推荐一下吧。

想了解更多的编程知识,你可以关注我,这样我写了新文章时你会得到通知。

这里看我所有的文章。

谢谢!

Amit Shekhar

你也可以在 Twitter, Linkedin, Github and Facebook找到我。

个人微信公众号已开通:CoderGhui ,欢迎关注!

版权声明

文章版权归本人所有,如需转载需在明显位置处保留作者信息及原文链接 !