美文网首页
定时器(注解方式)

定时器(注解方式)

作者: 寂静的春天1988 | 来源:发表于2020-06-12 12:06 被阅读0次

    原文:https://www.cnblogs.com/domi22/p/9418433.html

    1、spring boot使用注解实现定时器

    @EnableScheduling
    @Component
    public class TestTimer {
        @Scheduled(cron="*/1 * * * * ?")
        public void testOne() {
            System.out.println("第一个"+"每1秒执行一次"+Thread.currentThread().getName());
        }
     
        @Scheduled(fixedRate=1000)
        public void testTwo() {
            System.out.println("第二个"+"每1秒执行一次"+Thread.currentThread().getName());
        }
    }
    

    这样项目启动的时候定时器就启动了,但是通过打印线程名可以发现,这两个任务是同一个线程。

    如果将第二个任务睡眠10秒,模拟延时。

    @EnableScheduling
    @Component
    public class TestTimer {
        @Scheduled(cron="*/1 * * * * ?")
        public void testOne() {
            System.out.println("第一个"+"每1秒执行一次"+Thread.currentThread().getName());
        }
     
        @Scheduled(fixedRate=1000)
        public void testTwo() {
            System.out.println("第二个"+"每1秒执行一次"+Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    

    会发现不但自己的任务丢失了,也造成了其他任务的丢失。
    问题1:单线程任务丢失
    解决办法:采用异步的方式执行调度任务,配置 Spring 的 @EnableAsync,在执行定时任务的方法上标注 @Async配置任务执行池

    @EnableScheduling
    @EnableAsync
    @Component
    public class TestTimer {
        
        @Async
        @Scheduled(cron="*/1 * * * * ?")
        public void testOne() {
            System.out.println("第一个"+"每1秒执行一次"+Thread.currentThread().getName());
        }
        @Async
        @Scheduled(fixedRate=1000)
        public void testTwo() {
            System.out.println("第二个"+"每1秒执行一次"+Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    注意:@Async所使用的线程池容量为100
    3.配置线程池大小
    虽然上面的方式已经解决了我们的问题,但是总觉得不太好,有时候我们需要异步执行任务,但是又不需要这么多的线程的时候,我们可以使用下面的配置来设置线程池的大小
    配置文件:

    @Configuration
    public class AsyncConfig {
    
        @Bean("taskExecutor")
        public Executor taskExecutor1() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(200);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.setWaitForTasksToCompleteOnShutdown(true);
            executor.setAwaitTerminationSeconds(60);
            return executor;
        }
    
    }
    

    问题2:在分布式系统下,重复执行的问题。

    解决方式1:可以使用redis的分布式锁保证spring schedule集群只执行一次。 redis分布式锁是通过setnx命令实现的。该命令的作用是,当往redis中存入一个值时,会先判断该值对应的key是否存在,如果存在则返回0,如果不存在,则将该值存入redis并返回1。(但是在分布式跨时区部署的时候,依然无法避免重复执行)

    解决方式2:可以通过使用shedlock将spring schedule上锁。详细见:https://segmentfault.com/a/1190000011975027

    问题3:如果使用异步多线程方式,会有并发安全问题。
    如果想有的定时任务不考虑并发安全使用@Async注解,有的定时任务需要考虑线程并发使用同步阻塞方式。

    @EnableScheduling
    @EnableAsync 
    @Component
    public class TestTimer {
        
        @Scheduled(cron="*/1 * * * * ?")
        public void testOne() throws InterruptedException {
            System.out.println("任务1====>"+Thread.currentThread().getName());
            Thread.sleep(10000); //模拟延时
        }
        
        @Async
        @Scheduled(cron="*/1 * * * * ?")
        public void testTwo() throws InterruptedException {
            System.out.println("任务2====>"+Thread.currentThread().getName());
        }
    }
    

    发现任务2还是会被任务1阻塞住,好像没什么好办法?

    待解决:问题3

    相关文章

      网友评论

          本文标题:定时器(注解方式)

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