定时任务使用多线程注意事项

在定时任务中为了加快处理速度,一般都会使用多线程处理业务。需要注意一下事项:

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());

    }

 

转载于:https://my.oschina.net/skyzwg/blog/1616887

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值