SpringBoot系列——@Async优雅的异步调用

  前言

  众所周知,java的代码是同步顺序执行,当我们需要执行异步操作时我们需要创建一个新线程去执行,以往我们是这样操作的:

    /**
     * 任务类
     */
    class Task implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":异步任务");
        }
    }
        //新建线程并执行任务类
        new Thread(new Task()).start();

  jdk1.8之后可以使用Lambda 表达式

        //新建线程并执行任务类
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":异步任务");
        }).start();

  当然,除了显式的new Thread,我们一般通过线程池获取线程,这里就不再展开

  Spring 3.0之后提供了一个@Async注解,使用@Async注解进行优雅的异步调用,我们先看一下API对这个注解的定义:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html

  本文记录在SpringBoot项目中使用@Async注解,实现优雅的异步调用

  代码与测试

  项目工程结构

  因为要测试事务,所以需要引入

        <!--添加springdata-jpa依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--添加MySQL驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

  在启动类开启启用异步调用,同时注入ApplicationRunner对象在启动类进行调用测试

package cn.huanzi.qch.springbootasync;

import cn.huanzi.qch.springbootasync.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

@Component
@EnableAsync//开启异步调用
@SpringBootApplication
public class SpringbootAsyncApplication {

    @Autowired
    private TestService testService;

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

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");
            //无返回值
//            testService.asyncTask();

            //有返回值,但主线程不需要用到返回值
//            Future<String> future = testService.asyncTask("huanzi-qch");
            //有返回值,且主线程需要用到返回值
//            System.out.println(Thread.currentThread().getName() + ":返回值:" + testService.asyncTask("huanzi-qch").get());

            //事务测试,事务正常提交
//            testService.asyncTaskForTransaction(false);
            //事务测试,模拟异常事务回滚
//            testService.asyncTaskForTransaction(true);

            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }
}

  看一下我们的测试业务类TestService

package cn.huanzi.qch.springbootasync.service;

import java.util.concurrent.Future;

public interface TestService {
    /**
     * 异步调用,无返回值
     */
    void asyncTask();

    /**
     * 异步调用,有返回值
     */
    Future<String> asyncTask(String s);

    /**
     * 异步调用,无返回值,事务测试
     */
    void asyncTaskForTransaction(Boolean exFlag);
}
package cn.huanzi.qch.springbootasync.service;

import cn.huanzi.qch.springbootasync.pojo.TbUser;
import cn.huanzi.qch.springbootasync.repository.TbUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.Future;

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TbUserRepository tbUserRepository;

    @Async
    @Override
    public void asyncTask() {
        long startTime = System.currentTimeMillis();
        try {
            //模拟耗时
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + ":void asyncTask(),耗时:" + (endTime - startTime));
    }

    @Async("asyncTaskExecutor")
    @Override
    public Future<String> asyncTask(String s) {
        long startTime = System.currentTimeMillis();
        try {
            //模拟耗时
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + ":Future<String> asyncTask(String s),耗时:" + (endTime - startTime));
        return AsyncResult.forValue(s);
    }

    @Async("asyncTaskExecutor")
    @Transactional
    @Override
    public void asyncTaskForTransaction(Boolean exFlag) {
        //新增一个用户
        TbUser tbUser = new TbUser();
        tbUser.setUsername("huanzi-qch");
        tbUser.setPassword("123456");
        tbUserRepository.save(tbUser);

        if(exFlag){
            //模拟异常
            throw new RuntimeException("模拟异常");
        }
    }
}

  配置线程池

package cn.huanzi.qch.springbootasync.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * 线程池的配置
 */
@Configuration
public class AsyncConfig {

    private static final int MAX_POOL_SIZE = 50;

    private static final int CORE_POOL_SIZE = 20;

    @Bean("asyncTaskExecutor")
    public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
        asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
        asyncTaskExecutor.initialize();
        return asyncTaskExecutor;
    }
}

  配置好后,@Async会默认从线程池获取线程,当然也可以显式的指定@Async("asyncTaskExecutor")

  无返回值

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");
            //无返回值
            testService.asyncTask();
            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }

  有返回值

  有返回值,但主线程不需要用到返回值

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");//有返回值,但主线程不需要用到返回值
            Future<String> future = testService.asyncTask("huanzi-qch");

            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }

  有返回值,且主线程需要用到返回值

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");
//有返回值,且主线程需要用到返回值
            System.out.println(Thread.currentThread().getName() + ":返回值:" + testService.asyncTask("huanzi-qch").get());

            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }

  可以发现,有返回值的情况下,虽然异步业务逻辑是由新线程执行,但如果在主线程操作返回值对象,主线程会等待,还是顺序执行

  事务测试

  为了方便观察、测试,我们在配置文件中将日志级别设置成debug

#修改日志登记,方便调试
logging.level.root=debug

  事务提交

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");//事务测试,事务正常提交
            testService.asyncTaskForTransaction(false);

            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }

  模拟异常,事务回滚

    /**
     * 启动成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            long startTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":开始调用异步业务");
//事务测试,模拟异常事务回滚
            testService.asyncTaskForTransaction(true);

            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + ":调用异步业务结束,耗时:" + (endTime - startTime));
        };
    }

  后记

  SpringBoot使用@Async优雅的异步调用就暂时记录到这里,以后再进行补充

  代码开源

  代码已经开源、托管到我的GitHub、码云:

  GitHub:https://github.com/huanzi-qch/springBoot

  码云:https://gitee.com/huanzi-qch/springBoot

原文地址:https://www.cnblogs.com/huanzi-qch/p/11231041.html

时间: 2024-10-13 14:11:51

SpringBoot系列——@Async优雅的异步调用的相关文章

SpringBoot系列:Spring Boot异步调用@Async

在实际开发中,有时候为了及时处理请求和进行响应,我们可能会多任务同时执行,或者先处理主任务,也就是异步调用,异步调用的实现有很多,例如多线程.定时任务.消息队列等, 这一章节,我们就来讲讲@Async异步方法调用. 一.@Async使用演示 @Async是Spring内置注解,用来处理异步任务,在SpringBoot中同样适用,且在SpringBoot项目中,除了boot本身的starter外,不需要额外引入依赖. 而要使用@Async,需要在启动类上加上@EnableAsync主动声明来开启异

WCF系列教程之客户端异步调用服务

本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一.简介 在前面的随笔中,详细的介绍了WCF客户端服务的调用方法,但是那些操作全都是同步的,所以我们需要很长的时间等待服务器的反馈,如何一台服务器的速度很慢,所以客户端得到结果就需要很长的时间,试想一下,如果客户端是个web项目,那么客户体验可想而知,所以为了不影响后续代码执行和用户的体验,就需要使用异步的方式来调用服务.注意这里的异步是完全针对客户端而言的,与

springboot 异步调用Async使用方法

引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将介绍在springboot中如何使用@Async. 1.pom.xml中导入必要的依赖: <parent> <groupId>org.springframework.boot</groupId> <artifa

spring-boot实战【10】【转】:Spring Boot中使用@Async实现异步调用

什么是"异步调用"? "异步调用"对应的是"同步调用",同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行:异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序. 同步调用 下面通过一个简单示例来直观的理解什么是同步调用: 定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

170719、springboot编程之异步调用@Async

1.在pom.xml中增加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 2.在主类上开启注解 package com.rick; import org.springframework.boot.SpringApplication; imp

SpringBoot中异步请求和异步调用(看这一篇就够了)

一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应.一句话:增加了服务器对客户端请求的吞吐量(实际生产上我们用的比较少,如果并发请求量很大的情况下,我们会通过nginx把请求负载到集群服务的各个节点上来分摊请求压力,当然还可以通过消息队列来做请求的缓冲). 2.异步请求的实现 方式一:Servlet方式实现异步请求

springBoot定时任务和异步调用

springboot定时任务 在创建好的springboot项目的启动类上加@EnableScheduling注解. @EnableScheduling @SpringBootApplication @MapperScan("com.qianlong.dao") @ComponentScan(value = "com.qianlong") public class DemoApplication { public static void main(String[]

spring boot中使用@Async实现异步调用

什么是“异步调用”? “异步调用”对应的是“同步调用”,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行:异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序. 同步调用 下面通过一个简单示例来直观的理解什么是同步调用: 定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内) package com.dxz.demo1; import java.util.Random; import org.sprin

Spring中@Async注解实现“方法”的异步调用

简单介绍: Spring为任务调度与异步方法执行提供了注解支持.通过在方法上设置@Async注解,可使得方法被异步调用.也就是说调用者会在调用时立即返回,而被调用方法的实际执行是交给Spring的TaskExecutor来完成. 开启@Async注解: <task:annotation-driven executor="annotationExecutor" /> <!-- 支持 @Async 注解 --> <task:executor id="