Java多线程之任务分发
2018-02-26 本文已影响558人
南国的小狼
题引:
老师有3个同类任务,需要5个同学ABCDE分别都要处理完成。老师分配任务的策略是:每个任务都是随机分配下去的,每个同学拿到任务后都要完成这个任务。
像极了做数学题时的套路,这个题目其实就属于Java多线程之任务分发的模式。
在实际工作中,如果我们遇到了“要把多个同类任务分派给Java的多个线程去执行”的场景,需要思考如下的问题:
(1)待执行的任务列表是什么?
(2)需要启动多少Java线程去执行任务?
(3)每个Java线程实际要执行多少任务?(or:任务如何分发给每个Java线程?)
思考之后,我们还需要知道,Java多线程实现这种任务分发的一般策略是什么,为了便于下文叙述方便,我们先约定:
(1)任务数为taskNum
(2)计划启动线程数为planThreadNum
(3)实际启动线程数为realThreadNum
(1)确认实际启动的线程数:
如果taskNum < planThreadNum,则实际启动taskNum个线程,即一个线程执行一个任务;
如果taskNum众多,则实际启动planThreadNum个线程即可。
(2)确定每个线程执行的任务数:
如果taskNum < planThreadNum,每个线程都只执行一个任务;
如果taskNum众多,则先将taskNum按planThreadNum均分,余下的任务从前至后依次附加到线程中即可。
现在,我们既知道了多任务分发该思考哪些问题,也明白了Java多线程处理任务分发的一般策略,该如何解决题引中的问题呢?由题设和上文中的策略可知:
(1)我们需要一个Task类,来定义任务的状态(就绪、执行中、结束)和任务的行为(执行)
(2)我们需要一个Teacher类,来执行任务分配的工作
(3)我们需要一个StudentWorkThread类,来定义学生的工作线程,由该线程来执行任务
(4)我们需要一个DoTeacherWork的测试类,用来测试题引中的场景
(1)Task类:
public class Task {
private static final int READY = 0;
private static final int RUNNING = 1;
private static final int FINISHED = 2;
// 执行任务的状态
private int status;
// 用于标识任务
private int taskId;
public Task(int taskId) {
this.status = READY;
this.taskId = taskId;
}
public void execute() {
// 设置当前线程为执行状态
setStatus(Task.RUNNING);
System.out.println("线程ID:" + Thread.currentThread().getName() + ", 任务ID:" + this.taskId);
// 附加延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 执行结束
setStatus(Task.FINISHED);
}
public void setStatus(int status) {
this.status = status;
}
public int getTaskId() {
return taskId;
}
}
(2)Teacher类:
public class Teacher {
public static List[] distributeTasks(List taskList, int threadCount) {
// 每个线程至少执行的任务数
int minTaskCount = taskList.size() / threadCount;
// 平均分配后还剩余的任务数
int remainTaskCount = taskList.size() % threadCount;
// 实际启动的线程数,如果实际工作的线程大于工作任务数,则启动任务数个线程即可
int actualThreadCount = minTaskCount > 0 ? threadCount : remainTaskCount;
// 要启动的线程数组,以及每个线程要执行的任务列表
List[] perThreadWorkList = new List[actualThreadCount];
int taskIndex = 0;
// 任务剩余数重新声明标志,防止在执行过程中改变剩余线程数remainTaskCount的值
int remainIndces = remainTaskCount;
for (int i = 0; i < perThreadWorkList.length; i++) {
perThreadWorkList[i] = new ArrayList();
// 如果每个线程至少要执行的任务数>0,线程需要分配到基本任务
if (minTaskCount > 0) {
for (int j = taskIndex; j < minTaskCount + taskIndex; j++) {
perThreadWorkList[i].add(taskList.get(j));
}
}
taskIndex += minTaskCount;
// 还有剩余的任务,则补充一个到这个线程中
if (remainIndces > 0) {
perThreadWorkList[i].add(taskList.get(taskIndex++));
remainIndces--;
}
}
// 打印任务的分配情况
for (int i = 0; i < perThreadWorkList.length; i++) {
System.out.println("线程" + i + "任务数: " + perThreadWorkList[i].size() +
",区间[" + ((Task) perThreadWorkList[i].get(0)).getTaskId() + "," +
((Task) perThreadWorkList[i].get(perThreadWorkList[i].size() - 1)).getTaskId() + "]");
}
return perThreadWorkList;
}
}
(3)StudentWorkThread类:
public class StudentWorkThread extends Thread {
private List<Task> taskList = null;
private int threadId;
public StudentWorkThread(List<Task> taskList, int threadId) {
this.taskList = taskList;
this.threadId = threadId;
}
// 执行被指派的任务
public void run() {
for (Task task : taskList) {
task.execute();
}
}
}
(4)DoTeacherWork类:
public class DoTeacherWork {
public static void main(String[] args) {
// 构建老师的任务列表
List<Task> taskList = new ArrayList<Task>();
for (int i = 0; i < 3; i++) {
taskList.add(new Task(i));
}
System.out.println("老师的任务列表数:" + taskList.size());
// 设定学生工作的线程数为5
int threadNum = 5;
List[] perThreadWorkList = Teacher.distributeTasks(taskList, threadNum);
System.out.println("实际要启动的工作线程数:" + perThreadWorkList.length);
for (int i = 0; i < perThreadWorkList.length; i++) {
StudentWorkThread studentWorkThread = new StudentWorkThread(perThreadWorkList[i], i);
studentWorkThread.start();
}
}
}
结束语:题引中的任务分发只是一个简单的Java多线程应用场景,在实际工作中,在每一个思考环节的处理上,都可以进行扩展和细化需求。在此就不进行阐述了。