Java/Android中的优先级任务队列的实践
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java/Android中的优先级任务队列的实践相关的知识,希望对你有一定的参考价值。
队列的基本理解
用生活中的一个情景来举个栗子,前段时间很火爆的电视剧《人民的名义》中有一个丁义珍式的窗口大家应该都知道了,我们不说《人民的名义》也不说丁义珍,我们来说说这个办事窗口。
我们知道在某机构上班期间,窗口一直是开着的,有人去办事了窗口就开始做事,没人办事了窗口就处于等待的状态,如果去办事的人特别多,这些办事的人就必须排队(我特别讨厌在要排队的地方不排队的人,呼吁大家如果不是性命攸关的急事,一定要排队,谁不想早点办完事呢),窗口也可能是多个,而这些窗口中也可能有一些特别窗口,比如军人优先办理。
在说队列之前说两个名词:
Task
是任务,TaskExecutor
是任务执行器。
而我们今天要说的队列就完全符合某机构这个情况,队列在有Task
进来的时候TaskExecutor
就立刻开始执行Task
,当没有Task
的时候TaskExecutor
就处于一个阻塞状态,当有很多Task的时候Task也需要排队,TaskExecutor
也可以是多个,并且可以指定某几个Task
优先执行或者滞后执行。
综上所说我们得出一个这样的关系:队列相当于某机构,TaskExecutor相当于窗口,办事者
就是Task
。
到这里就结束啦,本文源码下载地址:
http://download.csdn.net/detail/yanzhenjie1003/9841188
项目源码使用IDEA写的,你可以直接import到你的IDEA,或者把源码直接拷贝到Eclipse或者androidStudio。
普通队列
当然很多机构也没有设置什么军人优先的窗口,所以队列也有不带优先级的队列,因此我们先来实现一个非优先级的队列。
我们常用的队列接口有:
Queue<E>
、BlockingQueue<E>
,基于上面我们说的特点,我们用BlockingQueue<E>
来实现任务队列,BlockingQueue<E>
的实现有很多,这里我们选择LinkedBlockingQueue<E>
。
和上述某机构不一样,某机构可以先有机构,再有窗口,再有办事者。但是我们写代码的时候,要想写一个队列,那么务必要在队列中写TaskExecutor
,那么就得先写好TaskExecutor
类,以此类推就得先有Task
类。
因此我们先写一个Task
的接口,也就是办事的人,我把它设计为接口,方便办各种不同事的人进来:
// 办事的人。
public interface ITask {
// 办事,我们把办事的方法给办事的人,也就是你要办什么事,由你自己决定。
void run();
}
接下来再写一个TaskExecutor
的类,也就是窗口,用来执行Task
,认真看注释,非常有助于理解:
// 窗口
public class TaskExecutor extends Thread {
// 在窗口拍的队,这个队里面是办事的人。
private BlockingQueue<ITask> taskQueue;
// 这个办事窗口是否在等待着办事。
private boolean isRunning = true;
public TaskExecutor(BlockingQueue<ITask> taskQueue) {
this.taskQueue = taskQueue;
}
// 下班。
public void quit() {
isRunning = false;
interrupt();
}
@Override
public void run() {
while (isRunning) { // 如果是上班状态就待着。
ITask iTask;
try {
iTask = taskQueue.take(); // 叫下一个办事的人进来,没有人就等着。
} catch (InterruptedException e) {
if (!isRunning) {
// 发生意外了,是下班状态的话就把窗口关闭。
interrupt();
break; // 如果执行到break,后面的代码就无效了。
}
// 发生意外了,不是下班状态,那么窗口继续等待。
continue;
}
// 为这个办事的人办事。
iTask.run();
}
}
}
这里要稍微解释下BlockingQueue<T>#take()
方法,这个方法当队列里面的item
为空的时候,它会一直处于阻塞状态,当队列中进入item
的时候它会立刻有一个返回值,它就和ServerSocket.accept()
方法一样,所以我们把它放入一个Thread
中,以免阻塞调用它的线程(Android中可能是主线程)。
办事的人和窗口都有了,下面我们封装一个队列,也就是某机构,用来管理这些窗口:
// 某机构。
public class TaskQueue {
// 某机构排的队,队里面是办事的人。
private BlockingQueue<ITask> mTaskQueue;
// 好多窗口。
private TaskExecutor[] mTaskExecutors;
// 在开发者new队列的时候,要指定窗口数量。
public TaskQueue(int size) {
mTaskQueue = new LinkedBlockingQueue<>();
mTaskExecutors = new TaskExecutor[size];
}
// 开始上班。
public void start() {
stop();
// 把各个窗口都打开,让窗口开始上班。
for (int i = 0; i < mTaskExecutors.length; i++) {
mTaskExecutors[i] = new TaskExecutor(mTaskQueue);
mTaskExecutors[i].start();
}
}
// 统一各个窗口下班。
public void stop() {
if (mTaskExecutors != null)
for (TaskExecutor taskExecutor : mTaskExecutors) {
if (taskExecutor != null) taskExecutor.quit();
}
}
// 开一个门,让办事的人能进来。
public <T extends ITask> int add(T task) {
if (!mTaskQueue.contains(task)) {
mTaskQueue.add(task);
}
// 返回排的队的人数,公开透明,让外面的人看的有多少人在等着办事。