趣学Spring:一文搞懂Aware、异步编程、计划任务(2)

简介: 趣学Spring:一文搞懂Aware、异步编程、计划任务

02、异步编程


“二哥,据说 Spring 可以通过 @Async 来实现异步编程,你能给我详细说说吗?”


“没问题啊。”


新建一个 AsyncService 类,内容如下:


public class AsyncService {
    @Async
    public void execute() {
        System.out.println(Thread.currentThread().getName());
    }
}



@Async 注解用在 public 方法上,表明 execute() 方法是一个异步方法。


新建一个 AsyncConfig 类,内容如下:


@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public AsyncService getAsyncService() {
        return new AsyncService();
    }
}



在配置类上使用 @EnableAsync 注解用以开启异步编程,否则 @Async 注解不会起作用。


新建一个 AsyncMain 类,内容如下:


public class AsyncMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);
        AsyncService service = context.getBean(AsyncService.class);
        for (int i = 0; i < 10; i++) {
            service.execute();
        }
    }



程序输出结果如下:


SimpleAsyncTaskExecutor-1

SimpleAsyncTaskExecutor-9

SimpleAsyncTaskExecutor-7

SimpleAsyncTaskExecutor-8

SimpleAsyncTaskExecutor-10

SimpleAsyncTaskExecutor-3

SimpleAsyncTaskExecutor-2

SimpleAsyncTaskExecutor-4

SimpleAsyncTaskExecutor-6

SimpleAsyncTaskExecutor-5



OK,结果符合我们的预期,异步编程实现了。就像你看到的那样,Spring 提供了一个默认的 SimpleAsyncTaskExecutor 用来执行线程,我们也可以在方法级别和应用级别上对执行器进行配置。


1)方法级别


新建 AsyncConfig 类,内容如下:


@Configuration

@EnableAsync
public class AsyncConfig {
    @Bean
    public AsyncService getAsyncService() {
        return new AsyncService();
    }
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        return executor;
    }
}


在配置类中创建了一个返回类型为 Executor 的 Bean,其名称定义为“threadPoolTaskExecutor”,并且重新设置了 ThreadPoolTaskExecutor 的核心线程池大小,默认为 1,现在修改为 5。


新进 AsyncService 类,内容如下:
public class AsyncService {
    @Async("threadPoolTaskExecutor")
    public void execute() {
        System.out.println(Thread.currentThread().getName());
    }
}


@Async 注解上需要指定我们之前配置的线程池执行器“threadPoolTaskExecutor”。


新建 AsyncMain 类,内容如下:


public class AsyncMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);
        AsyncService service = context.getBean(AsyncService.class);
        for (int i = 0; i < 10; i++) {
            service.execute();
        }
    }
}

程序运行结果如下:


threadPoolTaskExecutor-1

threadPoolTaskExecutor-2

threadPoolTaskExecutor-4

threadPoolTaskExecutor-3

threadPoolTaskExecutor-5

threadPoolTaskExecutor-3

threadPoolTaskExecutor-2

threadPoolTaskExecutor-4

threadPoolTaskExecutor-1

threadPoolTaskExecutor-5



从结果中可以看得出,线程池执行器变成了“threadPoolTaskExecutor”,并且大小为 5。


2)应用级别


新建 AsyncConfig 类,内容如下:


@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Bean
    public AsyncService getAsyncService() {
        return new AsyncService();
    }
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.initialize();
        return executor;
    }
}



需要实现 AsyncConfigurer 接口,并重写 getAsyncExecutor() 方法,这次设置线程池的大小为 3。注意执行器要执行一次 initialize() 方法。


新进 AsyncService 类,内容如下:


public class AsyncService {

   @Async

   public void execute() {

       System.out.println(Thread.currentThread().getName());

   }

}


新建 AsyncMain 类,内容如下:


public class AsyncMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);
        AsyncService service = context.getBean(AsyncService.class);
        for (int i = 0; i < 10; i++) {
            service.execute();
        }
    }
}



程序运行结果如下:


ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-2

ThreadPoolTaskExecutor-1

ThreadPoolTaskExecutor-3



从结果中可以看得出,线程池执行器变成了“ThreadPoolTaskExecutor”,并且大小为 3。


03、计划任务


“二哥,据说 Spring 可以通过 @Scheduled 来实现计划任务,你能给我详细说说怎么实现吗?”


“没问题啊。”


新建一个 ScheduledService 类,内容如下:


@Service
public class ScheduledService {
    @Scheduled(fixedDelay = 1000)
    public void scheduleFixedDelayTask() {
        System.out.println(
                "固定时间段后执行任务 - " + System.currentTimeMillis() / 1000);
    }
    @Scheduled(fixedRate = 1000)
    public void scheduleFixedRateTask() {
        System.out.println(
                "固定的频率执行任务 - " + System.currentTimeMillis() / 1000);
    }
    @Scheduled(cron = "0/2 * * * * ?")
    public void scheduleTaskUsingCronExpression() {
        long now = System.currentTimeMillis() / 1000;
        System.out.println(
                "Cron 表达式执行任务 - " + now);
    }
}


@Service 注解用于指定 ScheduledService 类为一个业务层的 Bean。@Scheduled 注解用于指定当前方法(返回类型为 void,无参)为一个任务执行方法,常见的用法有以下 3 种:


1)fixedDelay 用于确保任务执行的完成时间与任务下一次执行的开始时间之间存在 n 毫秒的延迟,下一次任务执行前,上一次任务必须执行完。


2)fixedRate 用于确保每 n 毫秒执行一次计划任务,即使最后一次调用可能仍在运行。


3)Cron 表达式比 fixedDelay 和 fixedRate 都要灵活,由 7 个部分组成,各部分之间用空格隔开,其完整的格式如下所示:


Seconds Minutes Hours Day-of-Month Month Day-of-Week Year

1

单词都很简单,就不用我翻译了。其中 Year 是可选项。常见的范例如下所示:


*/5 * * * * ?  每隔 5 秒执行一次

0 */1 * * * ?  每隔 1 分钟执行一次

0 0 23 * * ?  每天 23 点执行一次

0 0 1 * * ?  每天凌晨 1 点执行一次:

0 0 1 1 * ?  每月 1 号凌晨 1 点执行一次

0 0 23 L * ?  每月最后一天 23 点执行一次

0 0 1 ? * L  每周星期天凌晨 1 点执行一次

0 26,29,33 * * * ?  在 26 分、29 分、33 分执行一次

0 0 0,13,18,21 * * ? 每天的 0 点、13 点、18 点、21 点各执行一次


新建 ScheduledConfig 类,内容如下:


@Configuration
@EnableScheduling
@ComponentScan("high.scheduled")
public class ScheduledConfig {
}


@EnableScheduling 注解用于开启计划任务。@ComponentScan 注解用于扫描当前包下的类,如果它使用了注解(比如 @Service),就将其注册成为一个 Bean。


新建 ScheduledMain 类,内容如下:


public class ScheduledMain {

   public static void main(String[] args) {

       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScheduledConfig.class);

   }

}


程序运行结果如下:


固定的频率执行任务 - 1584666273

固定时间段后执行任务 - 1584666273

Cron 表达式执行任务 - 1584666274

固定的频率执行任务 - 1584666274

固定时间段后执行任务 - 1584666274

固定的频率执行任务 - 1584666275

固定时间段后执行任务 - 1584666275

Cron 表达式执行任务 - 1584666276


从结果中可以看得出,如果任务之间没有冲突的话,fixedDelay 任务之间的间隔和 fixedRate 任务之间的间隔是相同的,都是 1 秒;Cron 表达式任务与上一次任务之间的间隔为 2 秒。


“二哥,这篇文章中的示例代码你上传到 GitHub 了吗?”


“你到挺贴心啊,三妹。传送门~”


“二哥,你教得真不错,我完全学会了,一点也不枯燥。”


“那必须得啊,期待下一篇吧?”


“那是当然啊,期待,非常期待,望眼欲穿的感觉。”




请允许我热情地吐槽一下,这篇文章我不希望再被喷了,看在我这么辛苦搞原创(创意+干货+有趣)的份上,多鼓励鼓励好不好?别瞅了,点赞呗,你最美你最帅。


相关文章
|
8月前
|
传感器 Java API
Spring揭秘:Aware接口应用场景及实现原理!
Aware接口赋予了Bean更多自感知的能力,通过实现不同的Aware接口,Bean可以轻松地获取到Spring容器中的其他资源引用,像ApplicationContext、BeanFactory等。 这样不仅增强了Bean的功能,还提高了代码的可维护性和扩展性,从而让Spring的IoC容器变得更加强大和灵活。
290 0
Spring揭秘:Aware接口应用场景及实现原理!
|
8月前
|
Java Spring 容器
深入Spring原理-4.Aware接口、初始化和销毁执行顺序、Scope域
深入Spring原理-4.Aware接口、初始化和销毁执行顺序、Scope域
158 0
|
5月前
|
设计模式 自然语言处理 Java
简单了解下Spring中的各种Aware接口实现依赖注入
在Spring框架中,Aware接口是一组用于提供特定资源或环境信息的回调接口。这些接口被设计用来允许Bean获取对Spring容器或其他相关资源的引用,并在需要时进行适当的处理。
47 2
|
5月前
|
Java Spring 人工智能
AI 时代浪潮下,Spring 框架异步编程点亮高效开发之路,你还在等什么?
【8月更文挑战第31天】在快节奏的软件开发中,Spring框架通过@Async注解和异步执行器提供了强大的异步编程工具,提升应用性能与用户体验。异步编程如同魔法,使任务在后台执行而不阻塞主线程,保持界面流畅。只需添加@Async注解即可实现方法的异步执行,或通过配置异步执行器来管理线程池,提高系统吞吐量和资源利用率。尽管存在线程安全等问题,但异步编程能显著增强应用的响应性和效率。
61 0
|
5月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
138 0
|
前端开发 Java API
异步编程 - 11 Spring WebFlux的异步非阻塞处理2
异步编程 - 11 Spring WebFlux的异步非阻塞处理2
182 0
|
缓存 Java Spring
异步编程 - 09 Spring框架中的异步执行_@Async注解异步执行原理&源码解析
异步编程 - 09 Spring框架中的异步执行_@Async注解异步执行原理&源码解析
65 0
|
Java Spring
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇2
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇2
136 0
|
XML Java 数据格式
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
88 0
|
前端开发 Java API
异步编程 - 11 Spring WebFlux的异步非阻塞处理
异步编程 - 11 Spring WebFlux的异步非阻塞处理
508 0