一份基于quartz的任务多线程处理模板

本任务处理模板使用maven管理具体jar包依赖,使用quartz2.2.2搭建的一个定时任务处理模板,模板提供了一个CommonJob类用于quartz调用,此类的作用是处理任务模板类,规定了处理任务的步骤为:①获取待处理任务列表;②遍历待处理任务列表,逐一进行处理。然后只需要注入一个具体的任务类,此任务类可注入相应的业务处理service,service需实现CommonJobService中各个方法,service中相应方法可声明事物,以便模板类调用完成具体任务处理流程。


具体XML配置如下:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
   
    <context:component-scan base-package="com.defonds.scheduler" />  
    
    
    <!-- 具体的任务action需要实现commonJobService内各方法 -->
    <bean id="fileReadTask" class="com.action.FileReadTaskAction">
    	<property name="fileReadService" ref="fileReadService"></property>
    </bean>
    
    <bean id="fileReadService" class="com.service.impl.FileReadServiceImpl">
    </bean>
   
    <bean id="commonJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">
        	<!-- 为commonJob模板注入具体的任务action -->
        	<bean class="com.job.CommonJob">
        		<property name="autoTaskAction" ref="fileReadTask"></property>
        		<property name="threadPoolSize" value="5"></property>
        	</bean>
        </property>
		<property name="targetMethod" value="execute"></property>  
    </bean>  
   
    <!-- Run the job every 5 seconds -->  
    <bean id="cronTrigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
        <property name="jobDetail" ref="commonJob" />  
        <property name="cronExpression" value="0/5 * * * * ?" />        
    </bean>  
   
   
    <!-- Scheduler factory bean to glue together jobDetails and triggers to Configure Quartz Scheduler -->  
    <bean  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="jobDetails">  
            <list>  
                <ref bean="commonJob" />  
            </list>  
        </property>  
        <property name="triggers">  
            <list>  
                <ref bean="cronTrigger" />  
            </list>  
        </property>  
    </bean>  
   
</beans>  

commonJob类代码如下:

package com.job;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import com.common.AutoTask;
import com.common.Transaction;

public class CommonJob{
	
	private static Logger logger = LoggerFactory.getLogger(CommonJob.class);
	private String threadPoolSize;
	private AutoTask autoTaskAction;// 注入的任务
	private Map dupMap = new ConcurrentHashMap();
	
	// 定时任务执行入口方法
	public void execute() throws Exception{
		
		List<Object> list = autoTaskAction.fetchData();// 获取待处理状态任务数据
		ExecutorService threadPool = Executors.newFixedThreadPool(Integer.parseInt(threadPoolSize));
		
		// 遍历获取的待处理任务列表
		for(Object data:list){
			int uniqueId = autoTaskAction.getUniqueId(data);
			// 对任务做防重处理
			if(checkDup(uniqueId)){
				logger.debug("This transaction is under processing now : "+uniqueId);
				continue;// 若任务已在 处理,则暂不处理该任务,开始处理下一个任务
			}
			
			//检查任务是否为待处理状态(任务为状态驱动,再次确认任务是否为待处理状态,若是则继续处理,否则说明已正在被处理或已处理完)
			if(!checkStatus(uniqueId)){
				logger.debug("This transaction is not in wait-process status : "+uniqueId);
				continue;
			}
			// 将任务包装为Runnable
			RunnableTask task = new RunnableTask(data);
			// 提交给线程池执行
			threadPool.execute(task);
			
		}
		
		threadPool.shutdown();
		
	}
	
	protected boolean checkDup(int uniqueId){
		// job类内部维护一个防重表,若无此任务ID,则放置入Map,若已存在,则说明任务已在处理
		if(dupMap.get(uniqueId) == null){
			dupMap.put(uniqueId, "");
			return false;
		}else{
			return true;
		}
	}
	
	protected boolean checkStatus(int uniqueId){
		//根据ID获取任务详情
		Transaction t = autoTaskAction.loadTransactionById(uniqueId);
		//若任务状态仍未待处理则检查成功,继续处理任务,否则false,不处理该任务
		if("wait_process".equals(t.getStatus())){
			return true;
		}else{
			return false;
		}
		
	}
	
	public class RunnableTask implements Runnable{
		private Object data;
		// 构造方法需将任务实体传入
		public RunnableTask(Object data){
			this.data = data;
		}
		public void run() {
			int uniqueId = autoTaskAction.getUniqueId(data);
			try{
				// 调用注入的任务类处理任务方法处理任务
				autoTaskAction.deal(data);
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				// 处理完任务或抛出异常时均在防重map中将任务ID删除
				dupMap.remove(uniqueId);
			}
		}	
	}

	public void setThreadPoolSize(String threadPoolSize) {
		threadPoolSize = threadPoolSize;
	}

	public void setAutoTaskAction(AutoTask autoTaskAction) {
		this.autoTaskAction = autoTaskAction;
	}
	
}

该类提供了任务处理模板,其中execute方法为quartz定时任务入口方法(xml中设置),负责获取待处理任务列表,初始化线程池,遍历待处理任务列表,依次处理。其中我们将任务处理方法封装进内部Runnable类,以便利用线程池更快处理任务列表。任务处理时注意防重及任务状态确认,防止重复处理

以下为具体任务处理action及接口方法类:

package com.action;

import java.util.List;

import com.common.AutoTask;
import com.common.Transaction;
import com.service.CommonJobService;

public class FileReadTaskAction implements AutoTask{
	
	private CommonJobService fileReadService;

	public List<Object> fetchData() {
		List<Object> list = fileReadService.fetchData();
		return list;
	}

	public int getUniqueId(Object data) {
		Transaction t = (Transaction)data;
		return t.getTransId();
	}

	public void deal(Object data) {	
		fileReadService.deal(data);
	}

	public Transaction loadTransactionById(int transId) {
		
		return (Transaction)fileReadService.loadTransactionById(transId);
	}
	
	public void setFileReadService(CommonJobService fileReadService) {
		this.fileReadService = fileReadService;
	}

}
package com.common;

import java.util.List;

public interface AutoTask {

	public List<Object> fetchData();
	
	public int getUniqueId(Object data);
	
	public void deal(Object data);
	
	public Transaction loadTransactionById(int transId);
	
}
package com.service;

import java.util.List;

import com.common.Transaction;

public interface CommonJobService {
	
	public List<Object> fetchData();
	
	public void deal(Object data);
	
	public Transaction loadTransactionById(int transId);

}

最后为quartz的IOC容器启动类,通过run as java application即可实现定时处理任务

package com.auto;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App 
{
    public static void main( String[] args )
    {
    	ApplicationContext applicationContext = null;
		try {
			//获取配置文件,启动IOC容器
			applicationContext = new ClassPathXmlApplicationContext("classpath*:quartz.xml");

			System.out.println("AutoHandler is started successfully!!");
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
}


后修正了CommonJob.java的线程池生成方式,实现了InitializingBean接口,通过注解@value注入线程池PoolSize参数,让他在bean刚开始实例化后即产生线程池,完成任务列表后无需关闭线程池,等待下次任务周期再次复用即可。这样整个bean的生命周期只用创建一次固定大小线程池,省去了多次创建、销毁线程池的开销。代码如下:

package com.job;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;

import com.common.AutoTask;
import com.common.Transaction;

public class CommonJob implements InitializingBean{
	
	private static Logger logger = LoggerFactory.getLogger(CommonJob.class);
	@Value("${commonJob.threadPoolSize}")
	private String threadPoolSize;
	private AutoTask autoTaskAction;// 注入的任务
	private Map dupMap = new ConcurrentHashMap();
	private ExecutorService threadPool;
	
	// 定时任务执行入口方法
	public void execute() throws Exception{
		
		List<Object> list = autoTaskAction.fetchData();// 获取待处理状态任务数据
		//ExecutorService threadPool = Executors.newFixedThreadPool(Integer.parseInt(threadPoolSize));
		
		// 遍历获取的待处理任务列表
		for(Object data:list){
			int uniqueId = autoTaskAction.getUniqueId(data);
			// 对任务做防重处理
			if(checkDup(uniqueId)){
				logger.debug("This transaction is under processing now : "+uniqueId);
				continue;// 若任务已在 处理,则暂不处理该任务,开始处理下一个任务
			}
			
			//检查任务是否为待处理状态(任务为状态驱动,再次确认任务是否为待处理状态,若是则继续处理,否则说明已正在被处理或已处理完)
			if(!checkStatus(uniqueId)){
				logger.debug("This transaction is not in wait-process status : "+uniqueId);
				continue;
			}
			// 将任务包装为Runnable
			RunnableTask task = new RunnableTask(data);
			// 提交给线程池执行
			threadPool.execute(task);
			
		}
		
	}
	
	protected boolean checkDup(int uniqueId){
		// job类内部维护一个防重表,若无此任务ID,则放置入Map,若已存在,则说明任务已在处理
		if(dupMap.get(uniqueId) == null){
			dupMap.put(uniqueId, "");
			return false;
		}else{
			return true;
		}
	}
	
	protected boolean checkStatus(int uniqueId){
		//根据ID获取任务详情
		Transaction t = autoTaskAction.loadTransactionById(uniqueId);
		//若任务状态仍未待处理则检查成功,继续处理任务,否则false,不处理该任务
		if("wait_process".equals(t.getStatus())){
			return true;
		}else{
			return false;
		}
		
	}
	
	public class RunnableTask implements Runnable{
		private Object data;
		// 构造方法需将任务实体传入
		public RunnableTask(Object data){
			this.data = data;
		}
		public void run() {
			int uniqueId = autoTaskAction.getUniqueId(data);
			try{
				// 调用注入的任务类处理任务方法处理任务
				autoTaskAction.deal(data);
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				// 处理完任务或抛出异常时均在防重map中将任务ID删除
				dupMap.remove(uniqueId);
			}
		}	
	}

	/*public void setThreadPoolSize(String threadPoolSize) {
		threadPoolSize = threadPoolSize;
	}*/

	public void setAutoTaskAction(AutoTask autoTaskAction) {
		this.autoTaskAction = autoTaskAction;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		threadPool = Executors.newFixedThreadPool(Integer.parseInt(threadPoolSize));
		
	}
	
}





阅读更多
上一篇高并发系统优化思路
下一篇SpringMVC中servlet处理http请求源码解析
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭