• 51270

    文章

  • 499

    评论

  • 40

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

SpringBoot学习四:定时任务和异步调用

695856371Web网页设计师②群 | 172816590(满)Web网页设计师①群, 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

欢迎来到梁钟霖个人博客网站。本个人博客网站提供最新的站长新闻,各种互联网资讯。 还提供个人博客模板,最新最全的java教程,java面试题。在此我将尽我最大所能将此个人博客网站做的最好! 谢谢大家,愿大家一起进步!

相关文章

SpringBoot 学习一

SpringBoot 学习二:操作数据库

SpringBoot学习三:异常处理和记录日志

前言

今天来学习一下通过 SpringBoot 来实现一个定时任务和异步调用。

定时任务

在 Spring 中 可以通过 @EnableScheduling @Scheduled 这两个注解来实现我们的定时任务,接下来看下这两个注解:

@EnableScheduling

在配置类上标注了 @EnableScheduling 注解后,即表示 Spring 开启了定时任务,在 Spring 容器中标注了  @Scheduled 注解的方法都会被检测到,会进行执行;如果需要更多更加细粒度的控制,配置类可以实现 SchedulingConfigurer 类来进行实现,如下所示:

@Configuration
@EnableScheduling
public class MyScheduleTaskConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
//        taskRegistrar.setCronTasks();
//        taskRegistrar.setFixedDelayTasks();
//        taskRegistrar.setTriggerTasksList();
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
         return Executors.newScheduledThreadPool(100);
     }
}

 @Scheduled

在 Spring 中开启了定时任务后,就可以使用  @Scheduled  这个注解来定义需要定时执行的方法了,接下来看下它的一个定义:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    // cron 表达式
	String cron() default "";
    // 时区
	String zone() default "";
    // 时间间隔 单位毫秒
	long fixedDelay() default -1;
    // 时间间隔,单位毫秒,使用 String 表示
	String fixedDelayString() default "";
    // 时间间隔 单位毫秒
	long fixedRate() default -1;
    // 时间间隔,单位毫秒,使用 String 表示
	String fixedRateString() default "";
    // 初始化时间,即任务在多少秒后开始执行第一次
	long initialDelay() default -1;
    // 初始化时间,即任务在多少秒后开始执行第一次,使用 String 表示
	String initialDelayString() default "";

}


接下来,就来测试一下 cron ,fixedDelay 和 fixedRate 这三个属性:

cron 

cron 属性,即使用 cron 表达式来控制任务的定时执行,如下所示:

/**
 * 定时任务
 */
@Component
public class ScheduledTask {

    private static final DateFormat dateFormat = new SimpleDateFormat("HH:m:ss");

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }
}

启动程序,查看控制台打印,会 5 秒打印一次:

time: 14:19:00
time: 14:19:05
time: 14:19:10
time: 14:19:15

如果,任务在 5 秒之内已经执行完毕,会怎么样呢?

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

time: 14:20:50
time: 14:20:55
time: 14:21:00
time: 14:21:05

由打印的结果看到,如果任务在间隔时间之内已经执行完毕,则下一个任务也会等到时间间隔结束后才会执行下一次任务。

 

如果,任务在 5 秒之内还没有执行完,则会怎么样呢?

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

time: 14:28:25
time: 14:28:35
time: 14:28:45
time: 14:28:55

由打印结果可以看到,如果任务在时间间隔之内没有执行完毕,则下一个任务会留到下一个周期才会执行,因为 Spring 在处理使用cron表达式这种定时任务时,它会在任务开始时判断任务是否可以执行,如果可以则执行,如果不可以,那么它将不执行此次任务,等待下一次执行。

fixedDelay

fixedDelay 属性表示的是从上一个任务结束到下一个任务开始之间的时间间隔,如果上一个任务执行超时了,下一个任务会等待上一个任务执行完毕后才执行:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }

time: 14:38:29
time: 14:38:34
time: 14:38:39
time: 14:38:44
time: 14:38:49

如果,任务在时间间隔内执行完毕:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

每隔 7 秒打印一次

time: 14:40:24
time: 14:40:31
time: 14:40:38
time: 14:40:45
time: 14:40:52

从打印结果可以看到,如果任务在时间间隔之内已经执行完毕,下一个任务等待的时间将会是任务执行的时间+时间间隔;

如果,任务在之间间隔之内没有执行完毕:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

每隔 13 秒打印一次

time: 14:44:20
time: 14:44:33
time: 14:44:46
time: 14:44:59
time: 14:45:12

从打印结果可以看到,如果任务在时间间隔之内还没有执行完毕,下一个任务等待的时间将会是任务执行的时间+时间间隔;

从上面两个例子可以看到,fixedDelay  主要关注的是上一个任务结束的时间到下一个任务开始的时间之间的时间间隔,也就是下一个任务开始执行前需要等待的时间=上一个任务执行时间+时间间隔

fixedRate

fixedRate 表示的是两个任务开始执行的时间间隔,

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }

time: 14:53:38
time: 14:53:43
time: 14:53:48
time: 14:53:53
time: 14:53:58

和上面的 cron 和 fixedDelay 一样,都是 5 秒执行一次。

如果,任务在时间间隔之内已经执行完毕,如下:

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

time: 14:55:04
time: 14:55:09
time: 14:55:14
time: 14:55:19
time: 14:55:24

可以看到,这里就和 fixedDelay 不一样了,fixedDelay 下一个任务等待的时间等于上一个任务执行时间+时间间隔,而这里,从打印的结果可以看到,如果上一个任务在时间间隔之内已经执行完毕,则下一个任务还是会等待到时间间隔结束后才会执行。说明,fixedRate 表示的时间间隔是两个任务开始的时间间隔。

如果,任务在时间间隔之内没有执行完毕;

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

time: 15:0:24
time: 15:0:32
time: 15:0:40
time: 15:0:48
time: 15:0:56

从打印结果可以看到,如果任务在时间间隔之内还没有执行完毕,则下一个任务会等待上一个任务执行完毕后才开始执行,此时时间间隔不再是 5秒而是8秒。

结论

1、fixedRate 强调的是上一次任务的开始时间 到 下一次任务的开始时间的间隔
2、fixedDelay 调的是上一次任务的结束时间 到 下一次任务的开始时间的间隔
3、cron表达式配置了在哪一刻执行任务,会在任务开始时判断任务是否可以执行,如果能则执行,不能则会跳过本次执行

异步调用

在 Spring Boot 中通过 @EnableAsync@Async 这两个注解来实现异步调用的,接下来看下这两个注解:

@EnableAsync

@EnableAsync 该注解主要是用来开启Spring 异步调用的,用在配置类上,

@SpringBootApplication
@EnableAsync
public class MyspringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyspringbootApplication.class, args);
    }
}

@Async

当 Spring 开启了 异步调用后,就可以使用 @Async 定义需要异步执行的方法了,

/**
 * 异步调用任务
 */
@Component
public class AsyncTask {

    private Random random = new Random();

    @Async
    public void task1() throws InterruptedException {
        System.out.println("任务一开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务一耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务一完成...");
    }

    @Async
    public void task2() throws InterruptedException {
        System.out.println("任务二开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务二耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务二完成...");
    }

    @Async
    public void task3() throws InterruptedException {
        System.out.println("任务三开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务三耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务三完成...");
    }
}

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyspringbootApplication.class)
public class MyTest {

    @Autowired
    private AsyncTask task;

    @Test
    public void test() throws InterruptedException {
        task.task1();
        task.task2();
        task.task3();
        Thread.sleep(60 * 1 * 1000);
    }
}

任务一开始...
任务三开始...
任务二开始...
任务三耗时:2018
任务三完成...
任务一耗时:4027
任务一完成...
任务二耗时:9364
任务二完成...

 

如果不想使用默认的线程池,我们还可以自定义线程池,只需要实现 AsyncConfigurer 即可:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Bean(name = "taskExecutor")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        executor.initialize();
        // 关闭资源
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

使用自定义线程池:

    @Async("taskExecutor")
    public void task1() throws InterruptedException {
        System.out.println("任务一开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务一耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务一完成...");
    }

 

在线程执行的时候,可以有返回值 Future,我们可以返回 AsyncResult:

    @Async("taskExecutor")
    public Future<String> test4(){
        System.out.println("执行任务,返回结果");
        return new AsyncResult<>("任务执行结果:5");
    }

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyspringbootApplication.class)
public class MyTest {

    @Autowired
    private AsyncTask task;

    @Test
    public void test2() throws InterruptedException, ExecutionException, TimeoutException {
        Future<String> future = task.test4();
        System.out.println("任务是否完成:" + future.isDone());
        String result = future.get(5, TimeUnit.SECONDS);
        System.out.println(result);
        System.out.println("任务是否完成:" + future.isDone());
    }
}

结果:

任务是否完成:false
执行任务,返回结果
任务执行结果:5
任务是否完成:true

以上就是通过 SpringBoot 来操作定时任务和异步调用。


 转载至链接:https://my.oschina.net/mengyuankan/blog/2223408。


转载原创文章请注明出处,转载至: 梁钟霖个人博客www.liangzl.com

您觉喜欢本网站,或者觉得本文章对您有帮助,那么可以选择打赏。
打赏多少,您高兴就行,谢谢您对梁钟霖这小子的支持! ~(@^_^@)~

  • 微信扫一扫

  • 支付宝扫一扫

    支付宝打赏

2条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤
注册梁钟霖个人博客