使用多线程机制异步执行业务方法

    开发过程中,经常会遇到一键操作这样的功能,当数据量较少或者业务逻辑单一的时候没什么问题,但是当遇到数据量较大,而且业务逻辑较为复杂的情况,就比较棘手了,一键执行后,仿佛整个世界都在跟着转圈圈,直到请求超时,更有甚者,服务器直接驾崩。

    最近,在帮客户做微信会员资料更新操作时就遇到这样的情况,最开始是最简单的遍历执行,发现行不通;于是又换用分页批量执行的方法,结果发现换汤不换药,还是请求超时;最后没办发,只能通过使用多线程机制,通过开启多线程,以增加系统开销来节省请求时间,终于把问题解决了。也许会有更好的方法,但是目前实现功能要紧。话不多说,关门,放代码。。。

    对此,我写了两套方案,其一是通过实现Callable接口异步执行业务方法,最后返回执行结果;

package com.web.demo.thread;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 异步执行业务方法,实现Callable接口,返回执行结果
 * 
 * @author jiangyf
 */
public class AsyncTask implements Callable<List<Map<String, Object>>> {
	// 执行任务名称
	private String taskName;
	// 执行任务时间
	private long taskTime;
	// 线程同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
	private CountDownLatch latch;

	// 任务执行结果
	List<Map<String, Object>> resultList;
	private Map<String, Object> resultMap;

	public AsyncTask(String taskName, long taskTime, CountDownLatch latch) {
		super();
		this.taskName = taskName;
		this.taskTime = taskTime;
		this.latch = latch;
	}

	@Override
	public List<Map<String, Object>> call() throws Exception {
		resultList = new ArrayList<Map<String, Object>>();
		resultMap = new HashMap<String, Object>();
		// 任务开始时间
		long begin = System.currentTimeMillis();

		System.out.println(taskName + " 任务开始....");
		
		// 执行具体业务
		Thread.sleep(taskTime * 1000);
		
		System.out.println(taskName + " 任务结束....");

		// 任务结束时间
		long end = System.currentTimeMillis();
		taskTime = (end - begin) / 1000;
		resultMap.put("taskName", taskName);
		resultMap.put("taskTime", taskTime);
		resultList.add(resultMap);
		System.out.println(taskName + "任务用时:" + taskTime + "秒");
		if (latch != null) {
			// 任务完成,计数器减一
			latch.countDown();
		}
		return resultList;
	}

	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		// 任务开始时间
		long begin = System.currentTimeMillis();

		// 初始化计数器
		CountDownLatch latch = new CountDownLatch(2);
		// 初始化线程池
		ExecutorService executorService = Executors.newFixedThreadPool(2);
		// 初始化线程
		Future<List<Map<String, Object>>> future = executorService
				.submit(new AsyncTask("running", 2, latch));
		Future<List<Map<String, Object>>> future2 = executorService
				.submit(new AsyncTask("walking", 5, latch));
		executorService.shutdown();
		// 全部任务执行完成前,会一直阻塞当前线程,直到计时器的值为0
		latch.await();
		List<Map<String, Object>> result = future.get();
		List<Map<String, Object>> result2 = future2.get();
		result.addAll(result2);
		System.out.println(result.size());
		
		// 任务结束时间
		long end = System.currentTimeMillis();
		System.out.println("任务总用时:" + ((end - begin) / 1000) + "秒");
	}

}

其二是通过继承Thread类异步执行业务方法,最后不返回执行结果。

package com.web.demo.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;

/**
 * 异步执行业务方法,继承Thread类,不返回执行结果
 * 
 * @author jiangyf
 */
public class AsyncJob extends Thread {
	// 执行任务名称
	private String jobName;
	// 执行任务时间
	private long jobTime;
	// 线程同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
	private CountDownLatch latch;

	public AsyncJob(String jobName, long jobTime, CountDownLatch latch) {
		super();
		this.jobName = jobName;
		this.jobTime = jobTime;
		this.latch = latch;
	}

	public void run() {
		// 任务开始时间
		long begin = System.currentTimeMillis();

		System.out.println(jobName + " 任务开始....");

		// 执行具体业务
		try {
			Thread.sleep(jobTime * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println(jobName + " 任务结束....");

		// 任务结束时间
		long end = System.currentTimeMillis();
		jobTime = (end - begin) / 1000;
		System.out.println(jobName + "任务用时:" + jobTime + "秒");
		if (latch != null) {
			// 任务完成,计数器减一
			latch.countDown();
		}

	}

	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		// 任务开始时间
		long begin = System.currentTimeMillis();

		// 初始化计数器
		CountDownLatch latch = new CountDownLatch(2);
		// 初始化线程
		AsyncJob job = new AsyncJob("running", 5, latch);
		AsyncJob job2 = new AsyncJob("walking", 2, latch);
		job.start();
		job2.start();
		// 全部任务执行完成前,会一直阻塞当前线程,直到计时器的值为0
		latch.await();
		
		// 任务结束时间
		long end = System.currentTimeMillis();
		System.out.println("任务总用时:" + ((end - begin) / 1000) + "秒");
	}

}

以下为业务代码实现示例:

	// 会员资料更新失败的卡号
	private static StringBuffer cardNoStr = new StringBuffer();

	/**
	 * 同步微信会员信息
	 */
	public String syncVipInfo(String weixinId) {
		log.info("-------------同步微信会员信息开始");
		String msg = "";
		setWeixinInfo(weixinId);
		try {
			List<VipInfo> vipInfos = vipInfoDao.getByWeixinId(weixinId, null,
					null);
			int totalRows = vipInfos.size();
			msg = "需要同步的微信会员数:" + totalRows;
			log.info(msg);
			if (totalRows == 0) {
				return msg;
			}
			Map<String, String> map = PropertiesUtil
					.propertiesToMap("syncvipinfo.properties");
			if (map.get("max_num") == null || map.get("max_thread") == null) {
				msg = "同步微信会员信息配置文件错误";
				log.info(msg);
				return msg;
			}
			int offset = 0;
			int rows = Integer.parseInt(map.get("max_num"));
			int threadNum = Integer.parseInt(map.get("max_thread"));
			int count = totalRows / rows;
			if ((totalRows % rows) > 0) {
				count += 1;
			}
			if (count > threadNum) {
				if ((totalRows % threadNum) > 0) {
					count = threadNum - 1;
					rows = totalRows / count;
					if ((totalRows % count) > 0) {
						count += 1;
					}
				} else {
					count = threadNum;
					rows = totalRows / count;
				}
			}

			log.info("需要开启线程数量:" + count);
			// 任务开始时间
			long begin = System.currentTimeMillis();

			// 初始化计数器
			CountDownLatch latch = new CountDownLatch(count);
			
			// 初始化线程池
			ExecutorService executorService = Executors
					.newFixedThreadPool(count);
			// 初始化线程
			for (int i = 0; i < count; i++) {
				Future<String> future = executorService.submit(new AsyncTask(
						latch, vipInfos, offset, rows));
				offset += rows;
			}
			executorService.shutdown();
			log.info("线程池是否已关闭:" + executorService.isShutdown());
			
			// 初始化线程
			/*
			for (int i = 0; i < count; i++) {
				AsyncJob job = new AsyncJob(latch, vipInfos, offset, rows);
				job.start();
				offset += rows;
			}
			*/
			if (count > 0) {
				// 全部任务执行完成前,会一直阻塞当前线程,直到计时器的值为0
				latch.await();
			}

			if (!"".equals(cardNoStr.toString())) {
				msg = "同步微信会员信息失败的会员卡号有:" + cardNoStr.toString().substring(0, cardNoStr.lastIndexOf(","));
			} else {
				msg = "同步微信会员信息成功";
			}
			// 任务结束时间
			long end = System.currentTimeMillis();
			log.info("同步微信会员信息任务用时:" + ((end - begin) / 1000) + "秒");
		} catch (Exception e) {
			msg = "同步微信会员信息出现异常";
			log.error(msg + e.getMessage());
		}
		log.info("执行结果-------" + msg + "-------");
		log.info("-------------同步微信会员信息结束");
		return msg;
	}

	/**
	 * 异步执行业务方法,实现Callable接口,返回执行结果
	 * 
	 * @author jiangyf
	 */
	static class AsyncTask implements Callable<String> {
		private Logger log = LoggerFactory.getLogger(WXWebService.class);
		// 线程同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
		private CountDownLatch latch;
		private List<VipInfo> vipInfos;
		private int offset;
		private int rows;

		public AsyncTask(CountDownLatch latch, List<VipInfo> vipInfos,
				int offset, int rows) {
			super();
			this.latch = latch;
			this.vipInfos = vipInfos;
			this.offset = offset;
			this.rows = rows;
		}

		public String call() {
			// 任务开始时间
			long begin = System.currentTimeMillis();

			log.info("当前线程更新会员数量:------" + vipInfos.size());

			// 执行具体业务
			for (int i = 0; i < rows; i++) {
				int num = offset + i;
				if (num >= vipInfos.size()) {
					break;
				}
				VipInfo vipInfo = vipInfos.get(num);
				if (vipInfo != null) {
					String cardNo = vipInfo.getCardNo();
					WeixinUserInfo userInfo = getWeixinUserInfo(vipInfo
							.getOpenId());
					try {
						updateVipInfo(vipInfo, userInfo);
						log.info("会员卡号为" + cardNo + "的会员资料更新成功");
					} catch (SQLException e) {
						cardNoStr.append(cardNo + ",");
						log.info("会员卡号为" + cardNo + "的会员资料更新失败");
					}
				}
			}

			// 任务结束时间
			long end = System.currentTimeMillis();
			log.info("当前线程执行任务用时:" + ((end - begin) / 1000) + "秒");
			if (latch != null) {
				latch.countDown();// 任务完成,计数器减一
			}
			return cardNoStr.toString();
		}

	}

	static class AsyncJob extends Thread {
		private Logger log = LoggerFactory.getLogger(WXWebService.class);
		// 线程同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
		private CountDownLatch latch;
		private List<VipInfo> vipInfos;
		private int offset;
		private int rows;

		public AsyncJob(CountDownLatch latch, List<VipInfo> vipInfos,
				int offset, int rows) {
			super();
			this.latch = latch;
			this.vipInfos = vipInfos;
			this.offset = offset;
			this.rows = rows;
		}

		public void run() {
			// 任务开始时间
			long begin = System.currentTimeMillis();

			log.info("当前线程更新会员数量:------" + vipInfos.size());

			// 执行具体业务
			for (int i = 0; i < rows; i++) {
				int num = offset + i;
				if (num >= vipInfos.size()) {
					break;
				}
				VipInfo vipInfo = vipInfos.get(num);
				if (vipInfo != null) {
					String cardNo = vipInfo.getCardNo();
					WeixinUserInfo userInfo = getWeixinUserInfo(vipInfo
							.getOpenId());
					try {
						updateVipInfo(vipInfo, userInfo);
						log.info("会员卡号为" + cardNo + "的会员资料更新成功");
					} catch (SQLException e) {
						cardNoStr.append(cardNo + ",");
						log.info("会员卡号为" + cardNo + "的会员资料更新失败");
					}
				}
			}

			// 任务结束时间
			long end = System.currentTimeMillis();
			log.info("当前线程执行任务用时:" + ((end - begin) / 1000) + "秒");
			if (latch != null) {
				latch.countDown();// 任务完成,计数器减一
			}

		}
	}

 

代码地址:https://github.com/github-jade/myweb/tree/develop/src/main/java/com/web/demo/thread

 

转载于:https://my.oschina.net/jiangyf/blog/805423

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值