场景:多个任务场景,既有可并发执行任务也存在依赖关系的任务时。
例如app启动时的检查、目录检查、网络检查、用户信息获取、云端配置信息获取、更新检查、配置检查、设备信息检查。
其中大部分是可以进行并发执行的,但如获取用户信息、配置信息获取,等需要依赖网络检查任务。
如果在启动方法里完成这些,就显得很不优雅,代码也很难维护。这时候就需要一套可以并发自动调度的功能来满足。如果是一个一个写代码臃肿,不好维护,主要是不优雅。
在各种软件中这种场景还是比较多。
调用流程(这里可以是自己认为比较合理的优雅的调用流程,个人建议该流程一定不能复杂,以最低代码量进行设计。想要的结果)
创建原子任务(只管任务功能实现,如检测网络。只实现网络检查,返回执行成功与否,设置上一层依赖关系)->
创建任务调度实例 ->
添加原子任务进列表 ->
execute 执行结束。
设计思路
设计任务列表的排序算法。 为啥能想到拓扑排序算法。 原因是我分析了什么样的结果最合理,有向无环图 比较符合需求。
所以了解到了拓扑排序算法。 然后研究算法原理, 然后根据需求实现算法。topologicalSorting。这边写的比较粗糙,还能进行优化。 得到的拓扑排序结果后,就是任务调度。
按入度顺序依次调用,期间添加任务完成状态,确保后续任务的依赖是完成状态才可进行,代码还可以优化。目前的调度算法只是 按入度进行一排一排执行。
还可以优化成每个原子任务完成后继续调度后续任务。可以根据依赖完成度来决定是否启动下一个任务。这样会更快。 如 2 、4 依赖1 。3依赖 2. 因为2、4是并发任务,目前的设计只能支持2、4完成后在执行3 。其实3的执行和4没有关系。其实可以在2执行完成马上开始执行4,所以这边还可以进行优化来压榨cpu性能。 且还能添加 线程管理。运行的线程。等等 根据业务复杂来设计这个多线程任务调度框架。
具体代码实现
第一步设计原子任务接口
public interface AtomTask {
//任务
boolean run();
// 依赖
List<Class<?>> dependencies();
}
多线程 任务调度方法
public class MultithreadedTaskScheduling extends Activity {
/// 任务池
List<AtomTask> task = new LinkedList<>();
/// 新增
public void add(AtomTask atomTask) {
task.add(atomTask);
}
/// 拓扑排序算法
public List<TaskInfoStruct> topologicalSorting() {
List<TaskInfoStruct> topologicalTaskInfoList = new LinkedList<>();
int taskSize = task.size();
// 计算入度
for (int i = 0; i < taskSize; i++) {
TaskInfoStruct topologicalTaskInfo = new TaskInfoStruct();
AtomTask clsSelf = task.get(i);
int deep = 0;
List<Class<?>> clsList = clsSelf.dependencies();
if (clsList == null) {
topologicalTaskInfo.setTask(clsSelf);
topologicalTaskInfo.setPenetration(deep);
topologicalTaskInfoList.add(topologicalTaskInfo);
continue;
}
deep += 1;
// 这段代码可以优化
for (int k = 0; k < clsList.size(); k++) {
Class<?> item = clsList.get(k);
for (int l = 0; l < taskSize; l++) {
if (task.get(l).getClass().equals(item)) {
AtomTask someCls = task.get(l);
List<Class<?>> classList = someCls.dependencies();
int len = 0;
if (classList != null) {
len = classList.size();
}
deep += len;
}
}
}
topologicalTaskInfo.setTask(clsSelf);
topologicalTaskInfo.setPenetration(deep);
topologicalTaskInfoList.add(topologicalTaskInfo);
}
// 按入度进行排序
Collections.sort(topologicalTaskInfoList, new Comparator<TaskInfoStruct>() {
@Override
public int compare(TaskInfoStruct o1, TaskInfoStruct o2) {
return o1.getPenetration() - o2.getPenetration();
}
});
return topologicalTaskInfoList;
}
/// 启动调度
public void execute() throws InterruptedException {
// 获取拓扑排序结果
List<TaskInfoStruct> topologicalTaskInfoList = topologicalSorting();
// 分组执行
List<List<TaskInfoStruct>> topologicalTaskInfoMap = new LinkedList<>();
List<TaskInfoStruct> topologicalTaskInfoList1 = new LinkedList<>();
for (TaskInfoStruct item : topologicalTaskInfoList) {
if (topologicalTaskInfoList1.size() == 0) {
topologicalTaskInfoList1.add(item);
} else if (topologicalTaskInfoList1.get(0).getPenetration() == item.getPenetration()) {
topologicalTaskInfoList1.add(item);
} else {
topologicalTaskInfoMap.add(topologicalTaskInfoList1);
topologicalTaskInfoList1 = new LinkedList<>();
topologicalTaskInfoList1.add(item);
}
}
topologicalTaskInfoMap.add(topologicalTaskInfoList1);
HashMap<Class<?>, Boolean> okMap = new HashMap<>();
// 执行 这边还可以进行单任务调度优化。
for (List<TaskInfoStruct> itemList : topologicalTaskInfoMap) {
CountDownLatch countDownLatch = new CountDownLatch(itemList.size());
for (TaskInfoStruct item : itemList) {
List<Class<?>> list = item.getTask().dependencies();
if(list!=null){
boolean flag = false;
for (Class<?> i : list) {
flag = Boolean.TRUE.equals(okMap.get(i));
if (!flag) {
break;
}
}
// 放弃 因为有依赖执行失败了
if (!flag) {
okMap.put(item.getTask().getClass(), false);
countDownLatch.countDown();
continue;
}
}
//这里还可以优化成指定线程
new Thread(() -> {
boolean isOk;
// 处理错误 防止别的跑不下去
try {
isOk = item.getTask().run();
}catch (Exception e){
e.printStackTrace();
isOk = false;
}
okMap.put(item.getTask().getClass(), isOk);
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
}
}
}
任务数据结构
class TaskInfoStruct {
int penetration = 0;
AtomTask task;
public AtomTask getTask() {
return task;
}
public void setTask(AtomTask task) {
this.task = task;
}
public int getPenetration() {
return penetration;
}
public void setPenetration(int penetration) {
this.penetration = penetration;
}
}