前言

  众所周知,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

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

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

    在实际开发中,有时候为了及时处理请求和进行响应,我们可能会多任务同时执行,或者先处理主任务,也就是异步调用,异步调用的实现有很多,例如多线程.定时任务.消息队列等, 这一章节,我们就来讲讲@Async ...

  2. 使用Spring中@Async注解实现异步调用

    异步调用? 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,继 ...

  3. 【JDK8】Java8 优雅的异步调用API CompletableFuture

    1.CompletableFuture是什么? CompletableFuture是JDK8的新特性之一,是异步调用相关的API,用于简化异步调用,提高异步调用的效率 2.CompletableFut ...

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

    本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一.简介 在前面的随笔中,详细的介绍了WCF客户端服务的调用方法 ...

  5. SpringBoot系列: 如何优雅停止服务

    ============================背景============================在系统生命周期中, 免不了要做升级部署, 对于关键服务, 我们应该能做到不停服务完成 ...

  6. SpringBoot系列——事件发布与监听

    前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...

  7. SpringBoot学习笔记(十七:异步调用)

    @ 目录 1.@EnableAsync 2.@Async 2.1.无返回值的异步方法 2.1.有返回值的异步方法 3. Executor 3.1.方法级别重写Executor 3.2.应用级别重写Ex ...

  8. dubbo同步调用、异步调用和是否返回结果源码分析和实例

    0. dubbo同步调用.异步调用和是否返回结果配置 (1)dubbo默认为同步调用,并且有返回结果. (2)dubbo异步调用配置,设置 async="true",异步调用可以提 ...

  9. springboot 异步调用Async使用方法

    引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3. ...

随机推荐

  1. Qt 5.6.2 静态编译(VS2013 x86 target xp openssl icu webkit)

    在去年4月份的时候,我写过一篇动态编译Qt5.6.0的文章,当时是为了解决webkit不能在winxp下面跑的问题,动态编译有一个缺点,就是发布的时候,要携带一大堆dll,使安装包的体积增大.而静态编 ...

  2. js打印指定元素内容

    var v = document.createElement("div"); //向v中追加打印数据,可以将界面的元素追加进来 var h = window.open(" ...

  3. Spring Schema扩展机制

    1:概述 Spring2.0开始,Spring提供XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义 XML Bean解析器,集成到Spring IOC容器中. 2:步骤 ...

  4. centos安装最新版MySQL 8.0教程

    这篇教程是通过yum方式安装的 安装依赖 yum install libaio wget -y 检查MYSQL是否已安装 yum list installed | grep mysql 如果有先卸载 ...

  5. Java逆序输出整数

    题目要求:编写方法reverseDigit,将一个整数作为参数,并反向返回该数字.例如reverseDigit(123)的值是321.同时编写程序测试此方法. 说明:10的倍数的逆序,均以实际结果为准 ...

  6. F#周报2019年第26期

    新闻 逐渐演化的.NET Core框架 Visual Studio提示与技巧 Windows Termina(预览) Microsoft在GitHub上的工程师从2000名增加至25000名 视频及幻 ...

  7. 自定义实现一个loghub(或kafka)的动态分片消费者负载均衡?

    一般地,像kafka之类的消息中间件,作为一个可以保持历史消息的组件,其消费模型一般是主动拉取方式.这是为了给消费者足够的自由,回滚或者前进. 然而,也正是由于将消费消息的权力交给了消费者,所以,消费 ...

  8. C# 设计模式,简单工厂

    C# 实现计算机功能 (封装,继承,多态) using System; using System.Collections.Generic; using System.Linq; using Syste ...

  9. spring 5.x 系列第12篇 —— 整合memcached (代码配置方式)

    文章目录 一.说明 1.1 XMemcached客户端说明 1.2 项目结构说明 1.3 依赖说明 二.spring 整合 memcached 2.1 单机配置 2.2 集群配置 2.3 存储基本类型 ...

  10. 【Mysql】细节补充,约束、索引等

    约束: 显示建表语句:show create table 表名 查询表中的约束:SELECT * FROM information_schema.`TABLE_CONSTRAINTS`  where ...