`

spring mvc中的@async

 
阅读更多
引用

1、同步调用
下面通过一个简单示例来直观的理解什么是同步调用:

定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)

@Component
public class Task {

    public static Random random =new Random();

    public void doTaskOne() throws Exception {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }

}
在单元测试用例中,注入Task对象,并在测试用例中执行 doTaskOne 、 doTaskTwo 、 doTaskThree 三个函数。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-basic.xml")
@ActiveProfiles("dev")
public class ApplicationTests {

    @Autowired
    private Task task;

    @Test
    public void test() throws Exception {
        task.doTaskOne();
        task.doTaskTwo();
        task.doTaskThree();
    }

}
执行单元测试,可以看到类似如下输出:

开始做任务一
完成任务一,耗时:4256毫秒
开始做任务二
完成任务二,耗时:4957毫秒
开始做任务三
完成任务三,耗时:7173毫秒
任务一、任务二、任务三顺序的执行完了,换言之 doTaskOne 、 doTaskTwo 、 doTaskThree 三个函数顺序的执行完成。

2、异步调用
上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

在Spring中,我们只需要通过使用 @Async 注解就能简单的将原来的同步函数变为异步函数,Task类改在为如下模式:

@Component
public class Task {
    @Async
    public void doTaskOne() throws Exception {
        // 同上内容,省略
    }

    @Async
    public void doTaskTwo() throws Exception {
        // 同上内容,省略
    }

    @Async
    public void doTaskThree() throws Exception {
        // 同上内容,省略
    }

}
此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:

没有任何任务相关的输出
有部分任务相关的输出
乱序的任务相关的输出
原因是目前 doTaskOne 、 doTaskTwo 、 doTaskThree 三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。

注: @Async所修饰的函数不要定义为static类型 or 非public,这样异步调用不会生效

3、异步回调
为了让 doTaskOne 、 doTaskTwo 、 doTaskThree 能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。

那么我们如何判断上述三个异步调用是否已经执行完成呢?我们需要使用 Future 来返回异步调用的结果,就像如下方式改造 doTaskOne 函数:

@Component
public class Task {

    public static Random random =new Random();

    @Async
    public Future<String> doTaskOne() throws Exception { 
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务一完成");
    }

    public Future<String> doTaskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务二完成");
    }

    public Future<String> doTaskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务三完成");
    }

}
下面我们改造一下测试用例,让测试在等待完成三个异步调用之后来做一些其他事情。

@Test
public void test() throws Exception {

    long start = System.currentTimeMillis();

    Future<String> task1 = task.doTaskOne();
    Future<String> task2 = task.doTaskTwo();
    Future<String> task3 = task.doTaskThree();

    while(true) {
        if(task1.isDone() && task2.isDone() && task3.isDone()) {
            // 三个任务都调用完成,退出循环等待
            break;
        }
        Thread.sleep(1000);
    }

    long end = System.currentTimeMillis();

    System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");

}
看看我们做了哪些改变:

在测试用例一开始记录开始时间
在调用三个异步函数的时候,返回 Future 类型的结果对象
在调用完三个异步函数之后,开启一个循环,根据返回的 Future
对象来判断三个异步函数是否都结束了。若都结束,就结束循环;若没有都结束,就等1秒后再判断。
跳出循环之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。
执行一下上述的单元测试,可以看到如下结果:

开始做任务一
开始做任务二
开始做任务三
完成任务三,耗时:37毫秒
完成任务二,耗时:3661毫秒
完成任务一,耗时:7149毫秒
任务全部完成,总耗时:8025毫秒



注解的应用范围:
类:表示这个类中的所有方法都是异步的
方法:表示这个方法是异步的,如果类也注解了,则以这个方法的注解为准

相关的配置:
<task:annotation-driven />配置:
executor:指定一个缺省的executor给@Async使用。
例子:
<task:annotation-driven executor="asyncExecutor" />

<task:executor />配置参数:
id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。
pool-size:
core size:最小的线程数,缺省:1
max size:最大的线程数,缺省:Integer.MAX_VALUE
queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
rejection-policy:当pool已经达到max size的时候,如何处理新任务
ABORT(缺省):抛出TaskRejectedException异常,然后不执行
DISCARD:不执行,也不抛出异常
DISCARD_OLDEST:丢弃queue中最旧的那个任务
CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

配置例子:
<task:annotation-driven executor="asyncExecutor" />
<task:executor id="asyncExecutor" pool-size=" 100- 10000" queue-capacity=" 10"/>

实例:
<!-- 缺省的异步任务线程池 -->
<task:annotation-driven executor="asyncExecutor" />
<task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10" />

<!-- 处理log的线程池 -->
<task:executor id="logExecutor" pool-size="15-1000" queue-capacity="5" keep-alive="5"/>

@Override
@Async("logExecutor")    //如果不指定名字,会使用缺省的“asyncExecutor”
public void saveUserOpLog(TabUserOpLog tabUserOpLog) {

  userOpLogDAO.insertTabUserOpLog(tabUserOpLog);
}
分享到:
评论

相关推荐

    spring mvc 4.0

    8. **异步处理**:Spring MVC 4.0引入了异步请求处理,通过@ControllerAdvice和@Async注解,可以实现后台任务的异步执行,提高响应速度。 9. **多Part文件上传**:支持多文件上传,利用MultipartFile接口处理上传...

    Spring MVC异步模式

    在Spring MVC框架中,异步模式允许我们处理耗时的操作,如大数据处理、长时间的网络请求等,而不阻塞主线程。这种方式提高了系统的响应速度,改善了用户体验。本篇文章将深入探讨Spring MVC中的异步处理机制,以及...

    Mastering Spring MVC 4(2015.09)源码

    在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。在这个"Mastering Spring MVC 4(2015.09)源码"中,我们可以深入理解这个框架的核心概念和实际应用。 首先,Spring MVC ...

    spring MVC .docx

    14. **Asynchronous Request Processing**: Spring MVC支持异步请求处理,可以通过`@Async`注解实现后台任务的并发执行。 15. **Internationalization (i18n) and Localization (l10n)**: Spring MVC提供对国际化和...

    spring mvc

    15. **Async Support**: Spring MVC 提供异步处理支持,通过@Async注解可以将方法标记为异步执行,提高系统性能。 以上是Spring MVC框架的核心知识点,它们构成了一个功能强大且灵活的Web开发框架,使得开发者能够...

    spring mvc文件上传实现进度条

    在Spring MVC框架中,文件上传是一项常见的功能,而实现文件上传进度条则能提供更好的用户体验。这个场景通常涉及到前端的JavaScript或jQuery库(如jQuery File Upload)与后端的Spring MVC控制器之间的交互,以及...

    最全的Spring MVC注解例子,异步请求,错误处理

    - Spring MVC支持异步请求处理,主要通过`@Async`注解实现。这个注解可以应用在方法上,使该方法在一个单独的线程中执行,从而提高系统的并发能力。 - `@EnableAsync`:在配置类上添加此注解以启用异步支持。 - `...

    spring MVC基础学习

    在Spring MVC中,开发者可以利用模型-视图-控制器(MVC)架构模式来分离业务逻辑、数据处理和用户界面。下面我们将深入探讨Spring MVC的基础知识。 1. **核心组件**: - DispatcherServlet:作为Spring MVC的前端...

    Spring MVC 文件上传下载 后端 - Java.zip

    6. **优化**:为了提高性能,可以使用异步处理文件上传和下载,利用Spring MVC的AsyncController或者Servlet 3.0的异步处理特性。另外,对于大文件操作,可以考虑分块上传和下载。 7. **实际应用场景**:这些功能...

    spring mvc demo实例

    在Spring MVC中: - **模型(Model)**:负责处理业务逻辑和数据管理,通常与数据库交互。 - **视图(View)**:展示数据,通常使用JSP、Thymeleaf等模板引擎生成HTML页面。 - **控制器(Controller)**:接收用户...

    Spring MVC 框架学习总结

    16. **异步处理**:通过 @Async 和 TaskExecutor,Spring MVC 支持非阻塞的异步请求处理。 17. **测试**:Spring MVC 提供了 MockMVC,允许在没有实际服务器的情况下进行单元测试和集成测试。 以上就是 Spring MVC...

    spring3 mvc jar

    从压缩包子文件的文件名称列表"spring mvc jar"来看,可能包含的是Spring MVC的jar文件,这通常是开发过程中必需的依赖库,包含了所有必要的类和库,用于在Java环境中运行Spring MVC应用。 总之,Spring 3 MVC是一...

    java课件-15-spring MVC

    本课件旨在深入探讨Spring MVC的基本概念、工作原理及其在实际项目中的应用。 1. **Spring MVC基本架构** Spring MVC的核心组件包括DispatcherServlet、Controller、Model、View和ViewResolver。DispatcherServlet...

    Spring mvc web 框架

    在 Spring MVC 中,模型通常由 Java 对象(JavaBeans 或 POJOs)表示。 - **视图(View)**:负责呈现数据。Spring MVC 支持多种视图技术,如 JSP、FreeMarker、Thymeleaf 等,允许开发者选择最适合他们项目的视图...

    如果面试问你Spring MVC的原理

    10. **Async Support**:Spring MVC支持异步处理,可以通过`@Async`注解标记方法为异步,从而提高系统性能。 11. **MVC Annotation-driven**:Spring MVC提供了注解驱动的功能,使得开发者可以使用注解来简化配置,...

    对应Spring MVC 的博客

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建RESTful应用程序提供了强大的支持。...通过不断实践和学习,可以进一步掌握Spring MVC的高级特性,如Async support、WebFlux等,以适应不断发展的Web技术。

    基于注解的spring mvc

    在Web.xml文件中,我们需要设置几个关键的元素来启动Spring MVC的应用: 1. `&lt;context-param&gt;` 和 `&lt;contextLoaderListener&gt;`:这两个元素用于初始化Spring的ApplicationContext,它会加载指定的配置文件(在这个...

    spring-mvc框架

    Spring MVC支持异步处理,可以使用`@Async`注解和`DeferredResult`或`CompletableFuture`来处理异步任务。 **9. 模板引擎** Spring MVC可以配合各种模板引擎,如Thymeleaf、FreeMarker、JSP等,生成动态HTML页面。 ...

Global site tag (gtag.js) - Google Analytics