需求:
一个并发任务中心的框架,要求如下
-
提高性能,采用多线程,屏蔽细节
-
封装线程池和阻塞队列
-
每个批量任务拥有自己的上下文环境
-
需要一个并发安全的容器保存每个任务
-
自动清除已完成和过期任务
-
用户可以查询进度
如下两张图:

简单的类图设计如下
代码结构如下:
代码如下:
public class AppTest {
private final static String JOB_NAME = "计算数值";
private final static int JOB_LENGTH = 1000;
//查询任务进度的线程
private static class QueryResult implements Runnable {
private PendingJobPool pool;
public QueryResult(PendingJobPool pool) {
super();
this.pool = pool;
}
@Override
public void run() {
int i = 0;//查询次数
while (i < 350) {
List<TaskResult<String>> taskDetail = pool.getTaskResult(JOB_NAME);
if (!taskDetail.isEmpty()) {
System.out.println(pool.getTaskProcess(JOB_NAME));
System.out.println(taskDetail);
}
SleepTools.ms(100);
i++;
}
}
}
public static void main(String[] args) {
MyTask myTask = new MyTask();
//拿到框架的实例
PendingJobPool pool = PendingJobPool.getInstance();
//注册job
pool.register(JOB_NAME, JOB_LENGTH, myTask, 1000 * 5);
Random r = new Random();
for (int i = 0; i < JOB_LENGTH; i++) {
//依次推入Task
pool.putTask(JOB_NAME, r.nextInt(1000));
}
Thread t = new Thread(new QueryResult(pool));
t.start();
}
}
public class MyTask implements ITaskProcesser<Integer, Integer> {
@Override
public TaskResult<Integer> execute(Integer data) {
Random random = new Random();
int flag = random.nextInt(500);
SleepTools.ms(flag);
if (flag <= 300) {
Integer returnValue = data.intValue() + flag;
return new TaskResult<Integer>(returnValue, TaskResultType.Success);
} else if (flag > 301 && flag <= 400) {
return new TaskResult<Integer>("Failure", -1, TaskResultType.Failure);
} else {
try {
throw new RuntimeException("异常发生了!!");
} catch (Exception e) {
return new TaskResult<Integer>(e.getMessage(), -1, TaskResultType.Exception
);
}
}
}
}
public interface ITaskProcesser<T, R> {
TaskResult<R> execute(T data);
}
public class JobInfo<R> {
private AtomicInteger successCount;
private AtomicInteger taskProcesserCount;
private ITaskProcesser<?, ?> processer;
private LinkedBlockingDeque<TaskResult<R>> queue;
private int jobLength;
private long expireTime;
private String jobName;
public JobInfo(int jobLength, long expireTime, ITaskProcesser<?, ?> processer, String jobName) {
this.jobLength = jobLength;
this.expireTime = expireTime;
this.processer = processer;
this.jobName = jobName;
successCount = new AtomicInteger(0);
taskProcesserCount = new AtomicInteger(0);
queue = new LinkedBlockingDeque<TaskResult<R>>(jobLength);
}
public ITaskProcesser<?, ?> getTaskProcesser() {
return processer;
}
public int getSuccessCount() {
return successCount.get();
}
public int getTaskProcesserCount() {
return taskProcesserCount.get();
}
public int getFailureCount() {
return taskProcesserCount.get() - successCount.get();
}
public String getTotalProcess() {
return "Success[" + successCount.get() + "]/Current["
+ taskProcesserCount.get() + "] Total[" + jobLength + "]";
}
public List<TaskResult<R>> getTaskResult() {
TaskResult<R> taskResult;
List<TaskResult<R>> list = new LinkedList<>();
while ((taskResult = queue.pollFirst()) != null) {
list.add(taskResult);
}
return list;
}
public void addTaskResult(TaskResult<R> taskResult, CheckJobPorcesser checkJobPorcesser) {
if (taskResult.getType().equals(TaskResultType.Success)) {
successCount.getAndIncrement();
}
taskProcesserCount.getAndIncrement();
queue.addLast(taskResult);
if (taskProcesserCount.get() == jobLength) {
checkJobPorcesser.putJob(jobName, expireTime);
}
}
}
public class TaskResult<R> {
private String reason;
private R value;
private TaskResultType type;
public TaskResult(String reason, R value, TaskResultType type) {
this.reason = reason;
this.value = value;
this.type = type;
}
public TaskResult(R value, TaskResultType type) {
this.value = value;
this.type = type;
}
public String getReason() {
return reason;
}
public R getValue() {
return value;
}
public TaskResultType getType() {
return type;
}
@Override
public String toString() {
return "TaskResult{" +
"reason='" + reason + '\'' +
", value=" + value +
", type=" + type +
'}';
}
}
public enum TaskResultType {
Success,
Failure,
Exception;
}
public class CheckJobPorcesser {
private static DelayQueue<ItemVo<String>> queue = new DelayQueue<ItemVo<String>>();
private static class CheakJobHolder {
public static CheckJobPorcesser checkJob = new CheckJobPorcesser();
}
public static CheckJobPorcesser getInstance() {
return CheakJobHolder.checkJob;
}
public void putJob(String jobName, long expireTime) {
ItemVo<String> itemVo = new ItemVo<>(expireTime, jobName);
queue.offer(itemVo);
System.out.println("Job[" + jobName + "已经放入了过期检查缓存,过期时长:" + expireTime);
}
public static class FecthJob implements Runnable {
@Override
public void run() {
while (true) {
try {
ItemVo<String> itemVo = queue.take();
String jobName = itemVo.getData();
PendingJobPool.getMap().remove(jobName);
System.out.println(jobName + " is out of date,remove from map!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static {
Thread thread = new Thread(new FecthJob());
thread.setDaemon(true);
thread.start();
}
}
public class ItemVo<T> implements Delayed {
private long activeTime;//到期时间
private T data;
public ItemVo(long activeTime, T data) {
//将传人的持续时间毫秒转化为纳秒,+当前时间纳秒 = 过期时刻
this.activeTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(activeTime, TimeUnit.MILLISECONDS);
this.data = data;
}
public long getActiveTime() {
return activeTime;
}
public T getData() {
return data;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(activeTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
long l = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
return (l == 0) ? 0 : (l > 0) ? 1 : -1;
}
}
public class PendingJobPool {
private final int THREAD_NUM = Runtime.getRuntime().availableProcessors();
private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(5000);
private static ConcurrentHashMap<String, JobInfo<?>> jonInfoMap = new ConcurrentHashMap<>();
private ExecutorService executor = new ThreadPoolExecutor(THREAD_NUM, THREAD_NUM, 60L, TimeUnit.SECONDS, queue);
private static CheckJobPorcesser checkJobPorcesser = CheckJobPorcesser.getInstance();
private static class PeningTask<T, R> implements Runnable {
private JobInfo<R> jobInfo;
private T data;
public PeningTask(JobInfo<R> jobInfo, T data) {
this.jobInfo = jobInfo;
this.data = data;
}
@Override
public void run() {
R r = null;
ITaskProcesser<T, R> taskProcesser = (ITaskProcesser<T, R>) jobInfo.getTaskProcesser();
TaskResult<R> result = null;
try {
result = taskProcesser.execute(data);
if (null == result) {
result = new TaskResult<R>("result is null", r, TaskResultType.Exception);
}
if (null == result.getType()) {
if (null == result.getReason()) {
result = new TaskResult<R>("reason is null", r, TaskResultType.Exception);
} else {
result = new TaskResult<R>("result is null,but reason" + result.getReason(), r, TaskResultType.Exception);
}
}
} catch (Exception e) {
e.printStackTrace();
result = new TaskResult<R>(e.getMessage(), r, TaskResultType.Exception);
} finally {
jobInfo.addTaskResult(result, checkJobPorcesser);
}
}
}
private static class PendingPoolHolder {
private static PendingJobPool pendingJobPool = new PendingJobPool();
}
public static PendingJobPool getInstance() {
return PendingPoolHolder.pendingJobPool;
}
public static ConcurrentHashMap<String, JobInfo<?>> getMap() {
return jonInfoMap;
}
public <R> JobInfo<R> getJob(String jobName) {
JobInfo<R> jobInfo = (JobInfo<R>) jonInfoMap.get(jobName);
if (jobInfo == null) {
throw new RuntimeException("是个非法任务");
}
return jobInfo;
}
public <T, R> void putTask(String jobName, T t) {
JobInfo<R> job = getJob(jobName);
PeningTask<T, R> task = new PeningTask<>(job, t);
executor.execute(task);
}
public <R> void register(String jobName, int jobLength, ITaskProcesser<?, ?> processer, long expireTime) {
JobInfo<R> jobInfo = new JobInfo(jobLength, expireTime, processer, jobName);
if (jonInfoMap.putIfAbsent(jobName, jobInfo) != null) {
throw new RuntimeException(jobName + "已经注册了!");
}
}
public <R> List<TaskResult<R>> getTaskResult(String jobName) {
JobInfo<R> job = getJob(jobName);
return job.getTaskResult();
}
public <R> String getTaskProcess(String jobName) {
JobInfo<R> job = getJob(jobName);
return job.getTotalProcess();
}
}