java 多线程处理任务,返回带进度条结果 封装为工具类

应用场景

在我们写业务代码中,经常会想到多线程处理复杂耗时任务,但又想返回处理进度,下面代码就是实现这个业务。

以下代码是执行两个任务,分别输出两个任务的实时进度和最后结果,包括定时从缓存移除
运行效果图:
运行效果

类说明

需自定义类:

  • MyRun---------------程序测试入口
  • MyTask--------------具体需执行业务类
  • RequestData--------请求参数实体
  • ResponseRet-------返回结果实体

框架类(无需修改,直接用):

  • PendingJobPool-----------框架的主体类,也是调用者主要使用的类
  • CheckJobProcesser------任务完成后,在一定的时间供查询,之后为释放资源节约内存,需要定期处理过期的任务
  • ITaskProcesser-------------要求框架使用者实现的任务接口,因为任务的性质在调用时才知道
  • ItemDelayed----------------存放到延时队列的元素
  • JobInfo-----------------------任务信息实体
  • ResultEnum-----------------返回结果枚举类
  • TaskResult------------------任务处理返回结果实体类

MyRun类:

package com.hyun.test.my;

import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.hyun.test.PendingJobPool;
import com.hyun.test.vo.JobInfo;

/**
 * 类说明:模拟一个应用程序,提交工作和任务,并查询任务进度
 * @author hyun
 *
 */
public class MyRun {
	public static void main(String[] args) throws Exception {
		//模拟两个任务工作场景(例如两个人同时进行文件校验)
		MyTask task = new MyTask();
		String taskName = "test-1";
		String taskNameTwo = "test-2";
		//每个任务需要50个子任务(比如一个文件校验 可分为多个线程去校验)
		int taskLength = 50;
		//过期时间(比如页面需要查询校验进度,当页面不小心关了 可直接根据任务名称直接查询 不用再次校验,这个过期时间就是当当前未过期时能查询到)
		int expiredTime =10;
		//拿到框架的实例
		PendingJobPool<ResponseRet, RequestData> pool = PendingJobPool.getPendingTaskPool(ResponseRet.class, RequestData.class);
		PendingJobPool<ResponseRet, RequestData> poolTwo = PendingJobPool.getPendingTaskPool(ResponseRet.class, RequestData.class);
		//注册job
		pool.registerTask(taskLength, taskName, task, expiredTime, TimeUnit.SECONDS);
		poolTwo.registerTask(taskLength, taskNameTwo, task, expiredTime, TimeUnit.SECONDS);
		for(int i = 0; i < taskLength; i++) {
			RequestData data = new RequestData();
			data.setId(UUID.randomUUID().toString());
			data.setName("name1-" + i);
			//依次推入Task
			pool.putTask(taskName, data);
		}
		for(int i = 0; i < taskLength; i++) {
			RequestData data = new RequestData();
			data.setId(UUID.randomUUID().toString());
			data.setName("name2-" + i);
			pool.putTask(taskNameTwo, data);
		}
		//以下为查询结果操作
		Thread readData = new Thread(()->{
			for(;;) {
				JobInfo<ResponseRet, RequestData> result = pool.getJobInfoMap().get(taskName);
				JobInfo<ResponseRet, RequestData> resultTwo = pool.getJobInfoMap().get(taskNameTwo);
				System.out.println(resultTwo.getTaskName() + ":" + resultTwo.toString());
				System.out.println(result.getTaskName() + ":" + result.toString());
				if((result.sucessNum.get() + result.failNum.get()) >= taskLength && 
						(resultTwo.sucessNum.get() + resultTwo.failNum.get()) >= taskLength) {
					System.out.println(result.getTaskName() + ":" + result.getBlokingDeque().size() + ":" + result.getBlokingDequeDetail());
					System.out.println(resultTwo.getTaskName() + ":" + resultTwo.getBlokingDeque().size() + ":" + resultTwo.getBlokingDequeDetail());
					break;
				}
				
				Random random = new Random();
				try {
					Thread.sleep(random.nextInt(30));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		readData.start();
	}
}

MyTask类:

package com.hyun.test.my;

import java.util.Random;

import com.hyun.test.vo.ITaskProcesser;
import com.hyun.test.vo.ResultEnum;
import com.hyun.test.vo.TaskResult;
/**
 * 类说明:一个实际任务类,将数值加上一个随机数,并休眠随机时间 根据实际业务需修改类
 * @author hyun
 *
 */
public class MyTask implements ITaskProcesser<ResponseRet, RequestData>{

	@Override
	public TaskResult<ResponseRet> taskExecute(RequestData reqData) {
		TaskResult<ResponseRet> result = new TaskResult<ResponseRet>();
		String id = reqData.getId();
		String name = reqData.getName();
		Random rand = new Random();
		int randNum = rand.nextInt(100);
		//模拟实际操作  随机休眠100毫秒以内
		try {
			Thread.sleep(randNum);
			//如果20>随机数>=0 当做失败
			if(20 > randNum && randNum >= 0) {
				result.setResult(ResultEnum.FAIL);
				result.setReason("失败结果");
			} else if(randNum >= 90) {//如果随机数>=90 当异常处理
				result.setResult(ResultEnum.EXCEPTION);
				result.setReason("异常信息");
			} else {//其它情况当成功处理
				result.setResult(ResultEnum.SUCESS);
				result.setReason("Sucess");
				ResponseRet responseRet = new ResponseRet();
				responseRet.setResult(id);
				result.setData(responseRet);
			}
		} catch (InterruptedException e) {
			//异常处理
			e.printStackTrace();
			result.setResult(ResultEnum.EXCEPTION);
			result.setReason(e.getMessage());
		}
		return result;
	}
}

RequestData类:

package com.hyun.test.my;

/**
 * 请求参数实体 根据业务需修改类
 * @author hyun
 *
 */
public class RequestData {
	private String id;
	private String name;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "id:" + id + ",name:" + name;
	}
	
	
}

ResponseRet类:

package com.hyun.test.my;

/**
 * 返回结果实体 根据需要业务需修改类
 * @author hyun
 *
 */
public class ResponseRet {
	
	private String result;

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	@Override
	public String toString() {
		return "[result:" + result + "]";
	}
	
}

PendingJobPool类:

package com.hyun.test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.hyun.test.vo.ITaskProcesser;
import com.hyun.test.vo.ItemDelayed;
import com.hyun.test.vo.JobInfo;
import com.hyun.test.vo.ResultEnum;
import com.hyun.test.vo.TaskResult;

/**
 * 框架的主体类,也是调用者主要使用的类
 * @author hyun
 *
 * @param <R> 返回结果类型
 * @param <T> 参数类型
 */
public class PendingJobPool<R, T> {
	/**
	 * cpu核心数
	 */
	private final static int PROCESSOR_NUM = Runtime.getRuntime().availableProcessors() + 1;
	/**
	 * 初始化线程数
	 */
	private static int INIT_THREAD_NUM = PROCESSOR_NUM;
	/**
	 * keepAliveTime when the number of threads is greater than
     * the core, this is the maximum time that excess idle threads
     * will wait for new tasks before terminating
	 */
	private static int ALIVE_TIME = 5;
	/**
	 * 存放任务结果双端队列
	 */
	private static BlockingQueue<Runnable> bq = new ArrayBlockingQueue<>(2000);
	/**
	 * 线程池,固定大小,有界队列
	 */
	private Executor executor = new ThreadPoolExecutor(INIT_THREAD_NUM, PROCESSOR_NUM, ALIVE_TIME, TimeUnit.SECONDS, bq);
	/**
	 * job的存放容器
	 */
	public ConcurrentHashMap<String, JobInfo<R, T>> jobInfoMap = new ConcurrentHashMap<>();

	/**
	 * 单例模式
	 * @author hyun
	 *
	 * @param <R> 结果类型
	 * @param <T> 参数类型
	 */
	private static class InitPendingSingleton<R, T>{
		public static PendingJobPool<?,?> pendingTaskPool = new PendingJobPool<>();
	}
	
	@SuppressWarnings("unchecked")
	public static <E,K>PendingJobPool<E,K> getPendingTaskPool(Class<?> E, Class<?> K) {
		return (PendingJobPool<E,K>)InitPendingSingleton.pendingTaskPool;
	}
	/**
	 * 调用者注册工作,如工作名,任务的处理器等等
	 * @param taskLength
	 * @param taskName
	 * @param task
	 * @param expiredTime
	 * @param unit
	 * @throws Exception
	 */
	public void registerTask(int taskLength, String taskName, ITaskProcesser<R, T> task, long expiredTime, TimeUnit unit) throws Exception {
		JobInfo<R, T> jobInfo = new JobInfo<R, T>(taskLength, taskName, task, expiredTime, unit);
		JobInfo<R, T> oldJobInfo = this.jobInfoMap.putIfAbsent(jobInfo.getTaskName(), jobInfo);
		if(oldJobInfo != null) {
			throw new Exception("任务名称已存在");
		}
	}
	
	/**
	 * 对工作中的任务进行包装,提交给线程池使用,并处理任务的结果,写入缓存以供查询
	 * @author hyun
	 *
	 * @param <R> 结果类型
	 * @param <T> 参数类型
	 */
	private static class TaskThread<R, T> implements Runnable {
		final T data;
		private JobInfo<R, T> jobInfo;
		
		public TaskThread(JobInfo<R, T> jobInfo, T data) {
			this.data = data;
			this.jobInfo = jobInfo;
		}
		@Override
		public void run() {
			ITaskProcesser<R, T> taskProcesser = jobInfo.getTaskProcesser();
			TaskResult<R> result;
			try {
				//调用业务人员实现的具体方法
				result = taskProcesser.taskExecute(data);
				//要做检查,防止开发人员处理不当
				if (result == null) {
					result = new TaskResult<R>(ResultEnum.EXCEPTION, "result is null");
				}
				if(ResultEnum.SUCESS.equals(result.getResult())) {
					jobInfo.sucessNum.getAndIncrement();
				} else {
					jobInfo.failNum.getAndIncrement();
				}
			} catch (Exception e1) {
				e1.printStackTrace();
				jobInfo.failNum.getAndIncrement();
				result = new TaskResult<R>(ResultEnum.EXCEPTION, e1.getMessage());
			}
			try {
				jobInfo.getBlokingDeque().putLast(result);
				if((jobInfo.sucessNum.get() + jobInfo.failNum.get()) == jobInfo.getTaskLength() ) {
					ItemDelayed<String> delayed = new ItemDelayed<>(jobInfo.getExpiredTime(), jobInfo.getUnit(), jobInfo.getTaskName());
					CheckJobProcesser.bq.put(delayed);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * 调用者提交工作中的任务
	 * @param name
	 * @param data
	 */
	public void putTask(String name, T data) {
		JobInfo<R, T> jobInfo = this.jobInfoMap.get(name);
		executor.execute(new TaskThread<>(jobInfo, data));
	}

	public ConcurrentHashMap<String, JobInfo<R, T>> getJobInfoMap() {
		return jobInfoMap;
	}

	public void setJobInfoMap(ConcurrentHashMap<String, JobInfo<R, T>> jobInfoMap) {
		this.jobInfoMap = jobInfoMap;
	}
}

CheckJobProcesser类:

package com.hyun.test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;

import com.hyun.test.vo.ItemDelayed;

/**
 * 类说明:任务完成后,在一定的时间供查询,之后为释放资源节约内存,需要定期处理过期的任务
 * @author hyun
 *
 */
public class CheckJobProcesser {
	
	public static BlockingQueue<ItemDelayed<String>> bq = new DelayQueue<ItemDelayed<String>>();
	
	private static class InitCheckJobProcesser {
		public static CheckJobProcesser checkJobProcesser = new CheckJobProcesser();
	}
	
	/**
	 * 单例模式
	 * @return
	 */
	public static CheckJobProcesser getCheckJobProcesser() {
		return InitCheckJobProcesser.checkJobProcesser;
	}
	
	static {
		Thread thread = new Thread(new CheckJobThread());
		thread.start();
	}
	
	/**
	 * 处理队列中到期任务的执行
	 * @author hyun
	 *
	 */
	private static class CheckJobThread implements Runnable {
		@Override
		public void run() {
			try {
				System.out.println("检查任务过期线程启动");
				for(;;) {
					ItemDelayed<String> removeObj = bq.take();
					String taskName = removeObj.getData();
					PendingJobPool<Object,Object> pendingTaskPool = PendingJobPool.getPendingTaskPool(Object.class, Object.class);
					pendingTaskPool.getJobInfoMap().remove(taskName);
					System.out.println("任务被移除:" + taskName);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
}

ITaskProcesser类:

package com.hyun.test.vo;


/**
 *类说明:要求框架使用者实现的任务接口,因为任务的性质在调用时才知道,
 *所以传入的参数和方法的返回值均使用泛型
 * @author hyun
 *
 * @param <R> 返回结果类型
 * @param <T> 参数类型
 */
public interface ITaskProcesser<R, T> {
	/**
	 * @param data 调用方法需要使用的业务数据
	 * @return 方法执行后业务方法的结果
	 */
	public TaskResult<R> taskExecute(T data);

}

ItemDelayed类:

package com.hyun.test.vo;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 存放到延时队列的元素
 * @author hyun
 *
 * @param <T> 放入的数据类型
 */
public class ItemDelayed<T> implements Delayed {
	/**
	 * 到期时间
	 */
	private long activeTime;
	/**
	 * 数据实体
	 */
	private T data;
	
	public ItemDelayed(long activeTime, TimeUnit unit, T data) {
		this.data = data;
		//将传入的时长转换为超时的时刻
		this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime, unit) + System.nanoTime();//将传入的时长转换为超时的时刻
	}

	@Override
	public int compareTo(Delayed o) {
		long diffTime = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
		return diffTime > 0 ? 1 : ((diffTime == 0 ? 0 : -1));
	}

	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(this.activeTime - System.nanoTime(), TimeUnit.NANOSECONDS);
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

}

JobInfo类:

package com.hyun.test.vo;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 任务信息实体
 * @author hyun
 *
 * @param <R> 返回结果类型
 * @param <T> 参数类型
 */
public class JobInfo<R, T> {
	//工作的任务个数
	private final int taskLength;
	//区分唯一的工作
	private final String taskName;
	//成功处理的任务数
	public final AtomicInteger sucessNum = new AtomicInteger(0);
	//处理失败的任务数
	public final AtomicInteger failNum = new AtomicInteger(0);
	//这个工作的任务处理器
	private final ITaskProcesser<R, T> taskProcesser;
	//结果队列,拿结果从头拿,放结果从尾部放
	private final LinkedBlockingDeque<TaskResult<R>> blokingDeque;
	//工作的完成保存的时间,超过这个时间从缓存中清除
	private final long expiredTime;
	//过期时间单位
	private final TimeUnit unit;
	
	/**
	 * 阻塞队列不应该由调用者传入,应该内部生成,长度为工作的任务个数
	 * @param taskLength
	 * @param taskName
	 * @param taskProcesser
	 * @param expiredTime
	 * @param unit
	 */
	public JobInfo(int taskLength, String taskName, ITaskProcesser<R, T> taskProcesser, long expiredTime, TimeUnit unit) {
		super();
		this.taskLength = taskLength;
		this.taskName = taskName;
		this.sucessNum.set(0);
		this.failNum.set(0);
		this.taskProcesser = taskProcesser;
		this.blokingDeque = new LinkedBlockingDeque<TaskResult<R>>(taskLength);
		this.expiredTime = expiredTime;
		this.unit = unit;
	}
	
	/**
	 * 返回当前任务明细的结果 根据实际修改
	 * @return
	 */
	public String getBlokingDequeDetail() {
		StringBuffer sb = new StringBuffer();
		for(;;) {
			TaskResult<R> result = this.blokingDeque.pollFirst();
			if(result == null) {
				break;
			}
			sb.append(result.toString());
		}
		
		return sb.toString();
	}
	
	@Override
	public String toString() {
		return "Success[" + sucessNum.get() + "]/Fail[" + failNum.get() + "] Total[" + taskLength + "]";
	}

	public int getTaskLength() {
		return taskLength;
	}

	public String getTaskName() {
		return taskName;
	}

	public ITaskProcesser<R, T> getTaskProcesser() {
		return taskProcesser;
	}

	public LinkedBlockingDeque<TaskResult<R>> getBlokingDeque() {
		return blokingDeque;
	}
	
	public long getExpiredTime() {
		return expiredTime;
	}

	public TimeUnit getUnit() {
		return unit;
	}
}

ResultEnum类:

package com.hyun.test.vo;

/**
 * 返回结果枚举类
 * @author hyun
 *
 */
public enum ResultEnum {
	//方法成功执行并返回了业务人员需要的结果
	SUCESS,
	//方法成功执行但是返回的是业务人员不需要的结果
	FAIL,
	//方法执行抛出了Exception
	EXCEPTION;
}

TaskResult类:

package com.hyun.test.vo;

/**
 * 任务处理返回结果实体类
 * @author hyun
 *
 * @param <R> 返回结果类型
 */
public class TaskResult<R> {
	/**
	 * 方法的业务结果数据;
	 */
	private R data;
	/**
	 * 这里放方法失败的原因
	 */
	private String reason;
	/**
	 * 方法本身运行是否正确的结果类型
	 */
	private ResultEnum result;
	
	public TaskResult() {
		
	}
	public TaskResult(R data, String reason, ResultEnum result) {
		super();
		this.data = data;
		this.reason = reason;
		this.result = result;
	}
	
	public TaskResult(ResultEnum result, String reason) {
		super();
		this.reason = reason;
		this.result = result;
	}
	

	/**
	 * 方便业务人员使用,这个构造方法表示业务方法执行成功返回的结果
	 * @param data
	 */
	public TaskResult(R data) {
		super();
		this.data = data;
		this.reason = "sucess";
		this.result = ResultEnum.SUCESS;
	}

	@Override
	public String toString() {
		return "[result:" + result.name() + ",reason:" + reason + ",data:" + (data != null ? data.toString() : "") + "] ";
	}

	public String getReason() {
		return reason;
	}


	public void setReason(String reason) {
		this.reason = reason;
	}


	public ResultEnum getResult() {
		return result;
	}


	public void setResult(ResultEnum result) {
		this.result = result;
	}

	
	public void setData(R data) {
		this.data = data;
	}
	public R getData() {
		return data;
	}

}

猜你喜欢

转载自blog.csdn.net/huangyung/article/details/106315575