在一些对写有严格要求的业务中,每一次update都是一次特别谨慎的操作,比如在一次时间较长的数据计算中,发起一项计算后,就不需要重复的发起这项计算,一是节省资源,而是防止数据重复出现导致数据错误;
这时我们就会想到一个名词,线程,由于刚接触不久,这里就做一下简单的讲解,如有错误,欢迎指正。
在实战项目中线程池的应用分三个步骤:
1、创建单线程池类:
首先创建基线程:
public abstract class BaseComputeThread implements Runnable{/** * 任务名称 */ protected String taskName=""; public String getTaskName() { return taskName; } /** * 任务描述 */ protected String taskDescription=""; @Override public void run() { //判断依赖任务是否完成 checkDependTasks(); //任务执行 try {//开始任务 doJob(); } catch (Exception e) { System.out.println("异常了"); } } /** * 任务 * 此方法为虚方法,实际调用子类的实现 */ protected abstract void doJob(); }
所有的线程继承基类:
@Component public class TestThread extends BaseComputeThread { @Autowired private TestService testService; /** * 构造函数 */ public TestThread() { super.taskName = "TestThread"; super.taskDescription = "我要控制的方法名称"; } /** * 线程执行入口方法 * * @Exception 捕获异常 */ @Override public void run() { super.run(); } @Override protected void doJob() { testService.test(); } }
2、创建线程, 使用单线程池,防止同一类型的计算任务同时执行多次,并引入测试线程:
private ExecutorService testThreadpool = Executors.newSingleThreadExecutor(); @Autowired private TestThread testThread;
3、执行线程:
testThreadpool.execute(tsetThread);
拓展:
有的业务需求中不仅仅要求任务单线程执行,还需要某个方法在执行中,不让该方法再次执行,或者不让改方法一段时间内再次执行:
1、这时我们需要一个控制再次执行的类
public class ComputeTaskController { private final String SUCCESS ="提交后台执行任务成功!"; private final String RUNNING ="正在执行任务中,请稍后再试!"; /** * 用来存储计算任务的状态的HashMap * key为任务名称,value为状态+“;”+时间戳的字符串 */ private static HashMap<String, String> taskStateMap = new HashMap<String, String>(); /** * 判断任务是否需要执行 * 如果短时间内执行过可以不再执行 * @param taskName 任务名称 * @return * @throws Exception */ public static boolean checkTaskState(String taskName){ boolean ret = false; String taskState = taskStateMap.get(taskName); if(taskState==null){ //还没有执行过 ret = true; }else{ String[] tempsStrings = taskState.split(";"); if(tempsStrings[0].equals("0")){ //正在执行 ret = false; }else if(tempsStrings[0].equals("1")){ //任务完成 long lasttime = Long.parseLong(tempsStrings[1]); long curtime = new Date().getTime(); if(curtime-lasttime>1000 * 60 * 60){ //一小时内执行过就不再执行 //return false; } return true; }else if(tempsStrings[0].equals("2")){ //任务异常终止 ret = true; } } return ret; } /** * 设置任务运行状态 * @param taskName 任务名称 * @param state 任务状态 0任务开始;1任务完成;2任务异常终止 * @return * @throws Exception */ public static synchronized void setTaskState(String taskName, String state) { String curTimeString = String.valueOf(new Date().getTime()); taskStateMap.put(taskName, state+";"+curTimeString); } }
2、在需要执行任务时,修改上面第三步调用任务的方法:
public String runTestTask(HttpServletRequest request) { if (!ComputeTaskController.checkTaskState("TestThread")) { return "任务正在执行"; } else { testThreadpool.execute(testThread); } return "任务提交执行成功";
; }