在定时任务中为了加快处理速度,一般都会使用多线程处理业务。需要注意一下事项:
1. 定时任务是否允许上一个定时任务未结束,下一个定时任务可以启动,通过Scheduled中的配置在决定。
2. 主线程已经关闭,线程池中的线程还在运行问题。线程池的关闭方法问题
3. 定时任务有大量数据,导致服务无法停止问题。
4. 如何获取线程的处理结果
如下代码是示例,stop状态的使用和线程池shutdown的处理逻辑需要依据自己的业务来处理。
@PreDestroy
public void destory(){
stop = true;
}
//线程停止状态, 通过注解检测到服务器停止时修改stop状态
boolean stop = false;
//服务器启动后延迟1分钟执行,定时任务结束后延迟1分钟执行下一次
@Scheduled(initialDelay = 60*1000L, fixedDelay = 60*1000L)
public void scheduling(){
List<String> dataList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
dataList.add("data_"+i);
}
int threadSize = 10;
ExecutorService esPool = Executors.newFixedThreadPool(threadSize);
//接收线程的完成时间 或者其他返回结果
CompletionService<String> ecs = new ExecutorCompletionService<String>(esPool);
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>(dataList);
logger.info("===============start {}==================", System.currentTimeMillis());
//启动线程时修改退出线程状态
stop = false;
for (int i = 0; i < threadSize; i++) {
ecs.submit(()->{
long count = 0;
//线程处理加try catch 防止抛出异常中断线程,可能会导致线程池中所有的线程都中断,无可用线程
try{
// !queue.isEmpty()比queue.size()>0效率高很多 .size() 是要遍历一遍集合的
while (!stop && !queue.isEmpty()){
String data = queue.poll();
//500 可以在60秒内完成处理,正常退出
//改成 1000 如果不使用下面的收集结果代码,60秒内无法处理完,会强制shutdown 抛出异常
Thread.sleep(1000L);
logger.info("data {} ok.",data);
count++;
}
}catch (Exception e){
logger.error("",e);
}
//这里范围线程处理的结果
return System.currentTimeMillis()+"_"+count;
});
}
//获取线程的返回结果 会阻塞主线程,直到线程池中所有的线程返回结果
/*try {
for (int i = 0; i < threadSize; i++) {
String threadTime = ecs.take().get();
logger.info("thread run ok time:{}"+threadTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}catch (ExecutionException e) {
e.printStackTrace();
}*/
//关闭线程池
try {
esPool.shutdown();
logger.info("esPoll shutdown:{}", DateUtil.format(new Date(),DateUtil.PATTERN_DEFAULT));
//线程池阻塞,到指定的时间退出,如果所有线程执行完成返回true 否则返回false
boolean await = esPool.awaitTermination(60*1000L,TimeUnit.MILLISECONDS);
logger.info("esPool.awaitTermination 1:{}, {}",await,DateUtil.format(new Date(),DateUtil.PATTERN_DEFAULT));
if(!await) {
stop = true;
await = esPool.awaitTermination(10*1000L,TimeUnit.MILLISECONDS);
logger.info("esPool.awaitTermination 2:{}, {}",await,DateUtil.format(new Date(),DateUtil.PATTERN_DEFAULT));
}
if(!await){
logger.info("wait 60s not stop, shutdownNow");
// 超时的时候向线程池中所有的线程发出中断(interrupted)。
// 让线程池中的所有线程立即中断。 会抛出异常
esPool.shutdownNow();
}
} catch (InterruptedException e) {
//awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。
esPool.shutdownNow();
logger.error("awaitTermination",e);
}
logger.info("===============end {}==================", System.currentTimeMillis());
}