接上一篇【项目实战】 -- 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;
}
补充:
Java并发编程:Callable、Future和FutureTask