美文网首页后台开发笔记
@Scheduled多线程执行定时任务

@Scheduled多线程执行定时任务

作者: Figo_OU | 来源:发表于2021-04-09 16:03 被阅读0次

    @Scheduled 这个注解给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了。

    但是,我们需要注意的是,@Scheduled 并不一定一定会按时执行。

    关于cron表达式详解可以看这里:https://www.jianshu.com/p/1defb0f22ed1
    是不是懵了?我们看看代码

    //定时任务1,每秒运行一次
    @Scheduled(cron = "0/1 * * * * ?")
        public void autoRun() {
            Date date = new Date();
            Date oneMinLater = new Date(date.getTime() + 60 * 1000);
    //        runJob(()->{//分布式锁
                log.info("begin-------自动执行形象进度日记录");
                Date date1 = new Date();
                //模拟在做一个耗时很长的事情。
                while (oneMinLater.getTime() > date1.getTime()) {
                    date1 = new Date();
                }
                log.info("end-------自动执行形象进度日记录完成");
    //        },"形象进度");
        }
    
    //定时任务2,每5秒运行一次
    @Scheduled(cron = "0/5 * * * * ? ")
        public void checkBimModelTreeBuildTimeout() {
            String desc = "检查构建树超时情况";
    //        runJob(()->{
            log.info("begin-------{}" ,desc);
            log.info("end-------{}" ,desc);
    //        }, desc);
        }
    
    结果:
    15:38:17.001 [ozxThreadPool-1-thread-1] INFO  c.b.b.p.job.GraphicProgressJob - begin-------自动执行形象进度日记录
    
    而我们预期的结果:
    15:40:07.001 [ozxThreadPool-1-thread-1] INFO  c.b.b.p.job.GraphicProgressJob - begin-------自动执行形象进度日记录
    15:40:10.001 [ozxThreadPool-1-thread-2] INFO  c.b.b.p.j.BimModelTreeBuildTimeoutJob - begin-------检查构建树超时情况
    15:40:10.001 [ozxThreadPool-1-thread-2] INFO  c.b.b.p.j.BimModelTreeBuildTimeoutJob - end-------检查构建树超时情况
    15:40:15.002 [ozxThreadPool-1-thread-4] INFO  c.b.b.p.j.BimModelTreeBuildTimeoutJob - begin-------检查构建树超时情况
    15:40:15.002 [ozxThreadPool-1-thread-4] INFO  c.b.b.p.j.BimModelTreeBuildTimeoutJob - end-------检查构建树超时情况
    

    所以定时任务2并没有执行
    因为使用@Scheduled 的定时任务虽然是异步执行的,但是,不同的定时任务之间并不是并行的!!!!!!!!

    在其中一个定时任务没有执行完之前,其他的定时任务即使是到了执行时间,也是不会执行的,它们会进行排队。

    也就是如果你想你不同的定时任务互不影响,到时间就会执行,那么你最好将你的定时任务方法自己搞成异步方法,这样,

    定时任务其实就相当于调用了一个线程执行任务,一瞬间就结束了。当然,也可以勉强当做是任务都会定时执行。

    加上下面这个配置类就可以使定时任务变成异步的了

    @Configuration
    public class ScheduleConfig implements SchedulingConfigurer {
    
        @Value("${aib.threadPool.configList[1].name:schedule}")
        private String schedule;
    
        public static final String DEFAULT_SCHEDULE_POOL_NAME = "schedule";
        public static final Integer COREPOOLSIZE   = 8;
        public static final Integer MAXIMUMPOOLSIZE   = COREPOOLSIZE * 2;
        public static final Integer WAITQUEUECAPACITY   = 100;
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            ThreadTaskPool taskPool = ThreadTaskPools.getInstance().getTaskPool(schedule);
            if (null == taskPool) {
    //            return;
                ThreadPoolConfig threadPoolConfig = new ThreadPoolConfig("ozxThreadPool", ThreadTaskPoolType.SCHEDULE, COREPOOLSIZE,MAXIMUMPOOLSIZE,WAITQUEUECAPACITY);
                taskPool = new ThreadTaskPool(threadPoolConfig);
            }
            ThreadPoolExecutor threadPoolExecutor = taskPool.getThreadPoolExecutor();
            //创建scheduledExecutorService线程池
    //        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8, new PoolThreadFactory("ozxThreadPool"));
            //解决ScheduledExecutorService最大线程数Integer.MAX_VALUE问题
    //                  ((ScheduledThreadPoolExecutor) scheduledExecutorService).setMaximumPoolSize(16);
    
            scheduledTaskRegistrar.setScheduler(threadPoolExecutor);
        }
    }
    

    ps:【大家都说不要用Executors的各种构造方法去实例化线程池。其实主要的问题是newScheduledThreadPool的构造方法中是这么写的】

    public ScheduledThreadPoolExecutor(int corePoolSize,
                                           ThreadFactory threadFactory) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue(), threadFactory);//因为Integer.MAX_VALUE,所以可能会造成OOM
        }
    

    我的理解是只要修改最大线程数就好了。((ScheduledThreadPoolExecutor) scheduledExecutorService).setMaximumPoolSize(16);

    说明:Executors各个方法的弊端:
    1)newFixedThreadPool和newSingleThreadExecutor:
      主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
    2)newCachedThreadPool和newScheduledThreadPool:
      主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

    相关文章

      网友评论

        本文标题:@Scheduled多线程执行定时任务

        本文链接:https://www.haomeiwen.com/subject/dooikltx.html