第一步:定义一个线程池
package com.ucf.trade.util.common;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Component;
/**
* 线程池
* Description:
* All Rights Reserved.
* @version 1.0 2014年8月12日 下午3:57:16
*/
@Component
public class ThreadPool {
//阻塞队列深度
private static int maxQueueSize;
//核心线程数
private static int corePoolSize;
//最大线程数
private static int maximumPoolSize;
//线程池中的阻塞队列,请求数大于核心线程数时进阻塞队列等待
private static ArrayBlockingQueue<Runnable> queue;
//线程池
private static ThreadPoolExecutor pool;
static {
maxQueueSize = Integer.parseInt(Constant.getValue(Constant.ThreadPoolQueueSize));
corePoolSize = Integer.parseInt(Constant.getValue(Constant.ThreadPoolSize));
maximumPoolSize = Integer.parseInt(Constant.getValue(Constant.ThreadPoolSize));
queue = new ArrayBlockingQueue<Runnable>(maxQueueSize);
pool = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,3,TimeUnit.SECONDS,queue,new ThreadPoolExecutor.CallerRunsPolicy());//CallerRunsPolicy 超出线程池范围,立即开线程池外新线程处理,脱离线程池控制
}
/**
* 执行线程
* Description:
* @Version1.0 2014年8月12日 下午3:56:08 by lianjing(lianjing@ucfgroup.com)创建
* @param task
*/
public void exec(Runnable task) {
pool.execute(task);
}
/**
* 判断阻塞队列满
* Description:
* @Version1.0 2014年8月12日 下午3:56:29 by lianjing(lianjing@ucfgroup.com)创建
* @return
*/
public boolean isFull() {
return getQueue().size() == maxQueueSize;
}
public ArrayBlockingQueue<Runnable> getQueue() {
return queue;
}
public int getQueueSize() {
return queue.size();
}
}
package com.ucf.trade.util.common;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactory implements BeanFactoryAware {
//spring工厂
private BeanFactory beanFactory ;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public Object getBean(String name) {
return beanFactory.getBean(name);
}
public <T> T getBean(String name, Class<T> clazz) {
return (T)beanFactory.getBean(name);
}
}
第三步:定义任务类
package com.ucf.trade.schedule;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import com.ucf.platform.framework.core.log.UcfLogger;
import com.ucf.platform.framework.core.log.UcfLoggerFactory;
import com.ucf.trade.component.BatchPersistence;
import com.ucf.trade.pojo.UcfBatchOrderDetail;
import com.ucf.trade.util.common.Constant;
import com.ucf.trade.util.common.MyBeanFactory;
import com.ucf.trade.util.common.ThreadPool;
@Component
public class WithdrawBatchTask {
private final static UcfLogger logger = UcfLoggerFactory.getLogger(WithdrawBatchTask.class);
@Resource
private MyBeanFactory myBeanFactory;
@Resource
private ThreadPool threadPool;
@Resource
private BatchPersistence batchPersistence;
public void execTask() {
logger.info("【批量代发-后台任务】开始扫描待处理订单。。。");
try {
/*
* 1.判断队列深度
*/
if(threadPool.isFull()) {
logger.info("【批量代发-后台任务】当前线程池阻塞队列深度已达到最大,暂停扫描,队列深度为:" + threadPool.getQueueSize());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
//2.扫描待处理记录
List<UcfBatchOrderDetail> detailList = batchPersistence.queryDetailListForWait();
int detailSize = detailList.size();
logger.info("【批量代发-后台任务】本次扫描待处理订单数量为:" + detailSize);
//3.分配线程处理
for(UcfBatchOrderDetail detail : detailList) {
BatchExecutor batchExecutor = myBeanFactory.getBean("batchExecutor",BatchExecutor.class);
batchExecutor.setUcfBatchOrderDetail(detail);
logger.info("【批量代发-后台任务】merchantNo:{},加入线程队列,等待执行。", detail.getMerchantNo());
threadPool.exec(batchExecutor);
}
/*
* 4.持续扫描判断
* 本次没有记录,停顿5分钟
* 否则停顿时间为本次记录数除以最大线程数取整 + 1,单位秒
* 保证给每个线程留1秒处理时间
* 防止重复扫描到相同订单太多
*/
if(detailSize == 0) {
try {
Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
} else {
int poolSize = Integer.parseInt(Constant.getValue(Constant.ThreadPoolSize));
int sleepTime = detailSize / poolSize + 1;
try {
Thread.sleep(sleepTime * 1000);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
} catch(Exception e) {
logger.error(e.getMessage(), e);
logger.info("【批量代发-后台任务】处理订单出现异常:{}", e.getMessage());
}
logger.info("【批量代发-后台任务】本次扫描完成。");
}
}
第四步:定义具体的实现类
package com.ucf.trade.schedule;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.ucf.onlinepay.framework.common.util.StringUtil;
import com.ucf.platform.framework.core.log.UcfLogger;
import com.ucf.platform.framework.core.log.UcfLoggerFactory;
import com.ucf.trade.component.BatchPersistence;
import com.ucf.trade.dubbo.bo.WithdrawParamBo;
import com.ucf.trade.dubbo.bo.WithdrawReturnBo;
import com.ucf.trade.dubbo.service.WithdrawService;
import com.ucf.trade.pojo.UcfBatchOrder;
import com.ucf.trade.pojo.UcfBatchOrderDetail;
import com.ucf.trade.util.common.TimeUtil;
import com.ucf.trade.util.enums.TradeEnum;
import com.ucf.trade.util.exception.ErrorCode;
@Component
@Scope("prototype")
public class BatchExecutor implements Runnable {
private final static UcfLogger logger = UcfLoggerFactory.getLogger(BatchExecutor.class);
private UcfBatchOrderDetail ucfBatchOrderDetail;
@Resource
private BatchPersistence batchPersistence;
@Resource
private WithdrawService withdrawService;
@Resource(name = "sharedTransactionTemplate")
private TransactionTemplate transactionTemplate; // 手动提交事务
@Override
public void run() {
logger.info("【批量代发-后台任务】merchantNo:{},开始执行,调用单笔接口。", ucfBatchOrderDetail.getMerchantNo());
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
//1.锁批量明细表当前记录
UcfBatchOrderDetail lockDetail = new UcfBatchOrderDetail();
lockDetail = batchPersistence.queryOrderDetailAndLock(ucfBatchOrderDetail);
if(lockDetail == null || lockDetail.getStatus().equals(TradeEnum.PRE_PAY_SUCCESS.getCode())) {
return null;
}
//2.参数准备
WithdrawParamBo withdrawParamBo = new WithdrawParamBo();
BeanUtils.copyProperties(withdrawParamBo, ucfBatchOrderDetail);
withdrawParamBo.setBankCardNo(ucfBatchOrderDetail.getAccountNo());
withdrawParamBo.setBankCardUserName(ucfBatchOrderDetail.getAccountName());
withdrawParamBo.setSubType(TradeEnum.TRANS_WITHDRAW_SUBTYPE_BATCH.getCode());
//获取noticeUrl
UcfBatchOrder ucfBatchOrder = new UcfBatchOrder();
ucfBatchOrder.setBatchNo(ucfBatchOrderDetail.getBatchNo());
List<UcfBatchOrder> batchOrderList = batchPersistence.queryUcfBatchOrderByWhere(ucfBatchOrder);
if(batchOrderList.size() > 0) {
withdrawParamBo.setNoticeUrl(batchOrderList.get(0).getNoticeUrl());
}
//3.调用单笔代发
WithdrawReturnBo withdrawReturnBo = withdrawService.withdraw(withdrawParamBo);
//4.修改当前记录为已处理
UcfBatchOrderDetail updateCondition = new UcfBatchOrderDetail();
if(withdrawReturnBo.getResCode() != null && withdrawReturnBo.getResCode().equals(ErrorCode.OUT_ERROR_10005.getCode())) {
updateCondition.setStatus(TradeEnum.FAIL.getCode());
updateCondition.setResCode(withdrawReturnBo.getResCode());
updateCondition.setResMessage(withdrawReturnBo.getResMessage());
} else if(withdrawReturnBo.getStatus().equals(TradeEnum.WAIT.getCode())) {
updateCondition.setStatus(TradeEnum.PRE_PAY_SUCCESS.getCode());
} else {
if(StringUtil.isEmpty(withdrawReturnBo.getStatus())) {
updateCondition.setStatus(TradeEnum.FAIL.getCode());
updateCondition.setMessage(withdrawReturnBo.getResMessage());
updateCondition.setResCode(withdrawReturnBo.getResCode());
} else {
if(withdrawReturnBo.getStatus().equals(TradeEnum.SUCCESS.getCode())){
updateCondition.setStatus(withdrawReturnBo.getStatus());
}else{
updateCondition.setStatus(withdrawReturnBo.getStatus());
updateCondition.setMessage(withdrawReturnBo.getResMessage());
updateCondition.setResCode(withdrawReturnBo.getResCode());
}
}
updateCondition.setMessage(withdrawReturnBo.getResMessage());
}
updateCondition.setId(ucfBatchOrderDetail.getId());
if(!StringUtil.isEmpty(withdrawReturnBo.getTradeTime())) {
updateCondition.setTradeTime(TimeUtil.getDateFromDATETIME14(withdrawReturnBo.getTradeTime()));
}
updateCondition.setGmtModify(new Date());
batchPersistence.updateUcfBatchOrderDetail(updateCondition);
} catch(Exception e) {
status.setRollbackOnly();
}
return null;
}
});
}
public UcfBatchOrderDetail getUcfBatchOrderDetail() {
return ucfBatchOrderDetail;
}
public void setUcfBatchOrderDetail(UcfBatchOrderDetail ucfBatchOrderDetail) {
this.ucfBatchOrderDetail = ucfBatchOrderDetail;
}
}
部分常量定义:
public class Constant {
private final static UcfLogger logger = UcfLoggerFactory.getLogger(Constant.class);
public static String ThreadPoolSize = "ThreadPoolSize";
public static String ThreadPoolQueueSize = "ThreadPoolQueueSize";
public static String PerMaxCount = "PerMaxCount";
}
以上代码仅供自己参考