【项目实战】 -- 匿名内部类实现多线程操作

接上一篇【项目实战】 -- interface接口什么情况下可以被new?

起初,在执行task-历史消息归档任务时,使用匿名内部类实现了LoopCall的方法。其中processPageData方法中,对历史消息进行了多线程处理。

	@Override
	public void execute(TaskContext ctx) {
		logger.info("开始历史消息归档任务");
		Date minTime = TimeUtil.minusDay(new Date(), keepMaxDay);

		LoopPageExecutor.execute(new LoopCall<UserMsgHistory>() {

			@Override
			public List<UserMsgHistory> getBatch(int batchSize) {
				List<UserMsgHistory> batchList = historyDao.queryExistSendTimeEalier(minTime, batchSize);
				logger.info("待归档批次数:" + batchList.size());
				return batchList;
			}

			@Override
			public void processPageData(List<UserMsgHistory> data) {
				List<ConcurrentCall<Boolean>> calls = new LinkedList<>();
				for (UserMsgHistory his : data) {
					calls.add(new MsgArchiveCall(his, minTime));

				}
				ConcurrentExecutor.execute(calls, 30);
			}

		}, 3000);

	}
	/**
	 * 多线程,并发执行。<br/>
	 * 把calls分成多个分组,每个分组batchSize个数量。 每个分组多线程执行,多个分组之间是单线程执行<br/>
	 * 
	 * @param calls
	 * @param batchSize
	 * @return
	 */
	public static <V> List<V> execute(Collection<ConcurrentCall<V>> calls, int batchSize) {
		logger.debug("开始多线程任务,子任务数量:{}", calls.size());
		List<V> resultColl = new CopyOnWriteArrayList<V>();
		if (CollectionUtils.isEmpty(calls)) {
			return resultColl;
		}
		List<List<ConcurrentCall<V>>> deviceCalls = CollectionConventer.devide(calls, batchSize);
		for (List<ConcurrentCall<V>> batch : deviceCalls) {
			List<V> batchResult = execute(batch);
			resultColl.addAll(batchResult);
		}
		return resultColl;
	}
	/**
	 * 多线程,并发执行。等到所有现成结束,会继续主线程<br/>
	 * 如果只有一个子任务,则不开启并发线程执行。采用单线程执行<br/>
	 * 
	 * @param calls
	 */
	public static <V> List<V> execute(Collection<ConcurrentCall<V>> calls) {
		logger.debug("开始多线程任务,子任务数量:{}", calls.size());
		List<V> resultColl = new CopyOnWriteArrayList<V>();
		if (CollectionUtils.isEmpty(calls)) {
			return resultColl;
		}

		if (calls.size() == 1) {
			// 如果只有一个子任务,则不开启并发线程执行
			ConcurrentCall<V> call = calls.iterator().next();
			try {
				CountDownLatch cdl = new CountDownLatch(1);
				call.setCdl(cdl);
				V v = call.call();
				resultColl.add(v);
			} catch (Exception e) {
				e.printStackTrace();
				logger.error("任务执行失败,{}", e);
				// 吞并这个异常,返回空的list列表
			}
			return resultColl;
		} else {
			CountDownLatch cdl = new CountDownLatch(calls.size());
			List<Future<V>> futures = new LinkedList<>();
			for (ConcurrentCall<V> call : calls) {
				call.setCdl(cdl);
				Future<V> future = executor.submit(call);
				futures.add(future);
			}
			try {
				logger.debug("开始等待子线程结束");
				cdl.await();
				logger.debug("子线程全部结束,继续主线程");
				for (Future<V> f : futures) {
					resultColl.add(f.get());
				}

				return resultColl;
			} catch (InterruptedException | ExecutionException e) {
				logger.warn("主线程出现异常,{}", e);
				throw new PushInnerException(e);
			}
		}
	}
	/**
	 * 所有需要并发执行的,需要继承此方法
	 */
	@Data
	public static abstract class ConcurrentCall<V> implements Callable<V> {
		private CountDownLatch cdl;

		@Override
		public V call() throws Exception {
			try {
				V v = this.doCall();
				return v;
			} catch (Throwable t) {
				if (t instanceof PushInnerException) {
					logger.error(t.getMessage());
				} else {
					logger.error("unknown exception", new MailAlarmAppender.MailAlarmException(t));
				}

				throw new RuntimeException(t);
			} finally {
				this.cdl.countDown();
			}

		}

		public abstract V doCall() throws Exception;

	}

补充:

CopyOnWriteArrayList的原理和使用方法

Java并发编程:Callable、Future和FutureTask

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值