一、背景
并发基础学了一段时间,接下来实战一把
业务背景:下载多个文档,如果使用单线程一个个下载,太慢了,我们实现一个并发框架
二、业务要求
可以查询进度,下载了多少个,以及有多少个成功
完成后缓存一段时间,过期清理
三、着手
3.1 接口ITaskProcesser
回想使用其他框架,如spring等,都会提供一些接口,我们开发人员只需要使用对应的接口,即可完成相应的功能。这里我们的并发框架同样提供一个接口,给开发人员实现
public interface ITaskProcesser<T, R> {
TaskResult<R> taskExecute(T data);
}
其中,T是入参,R是业务方法执行后的结果。taskExecute是业务逻辑,比如下载文档
这些都是开发人员自己实现
3.2 结果类
无论是什么业务,结果都可以概括为三种:业务方法执行结束且成功,业务方法执行结束但失败,业务方法出现异常
public enum TaskResultType {
//方法成功执行并返回了业务人员需要的结果
Success,
//方法成功执行但是返回的是业务人员不需要的结果
Failure,
//方法执行抛出了Exception
Exception;
}
但是这样还不够,比如如果失败了,失败原因是什么。如果成功了,返回结果是什么
所以我们需要再次封装一下
public class TaskResult<R> {
//这里设置为final,是因为当任务执行完后,这些都是确定的,不需要业务人员去修改
//方法本身运行是否正确的结果类型
private final TaskResultType resultType;
//方法的业务结果数据,使用泛型
private final R returnValue;
//如果失败,失败的原因
private final String reason;
public TaskResult(TaskResultType resultType, R returnValue, String reason) {
super();
this.resultType = resultType;
this.returnValue = returnValue;
this.reason = reason;
}
//方便业务人员使用,这个构造方法表示业务方法执行成功返回的结果
public TaskResult(TaskResultType resultType, R returnValue) {
super();
this.resultType = resultType;
this.returnValue = returnValue;
this.reason = "Success";
}
public TaskResultType getResultType() {
return resultType;
}
public R getReturnValue() {
return returnValue;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return "TaskResult [resultType=" + resultType+ ", returnValue=" + returnValue
+ ", reason=" + reason + "]";
}
}
这里注意:我们使用了final去修饰属性,因为当任务结束,这些属性都是确定的,不需要让开发人员去修改。同理,只暴露了get方法,并没有set方法
3.3 工作类
每个用户提交给框架的,我们称之为一个工作
一个工作可能包含多个任务
比如A提交一个工作给框架,下载300个文档。B同样可以提交一个工作给框架,下载500个文档
这样的话,工作类就需要以下属性
工作名称 |
类似于id,用于区分A员工提交的工作和B员工提交的工作 |
工作任务个数 |
比如A员工提交的工作下载100个文档,任务个数就是100 |
工作的任务处理器 |
比如A员工提交的工作下载100个文档,每个任务是下载,但是B员工提交的工作可能是上传。所以需要开发人员自己实现自己的任务处理器 |