@Async优雅的异步调用

  • A+
所属分类:Java 多线程

  前言

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

@Async优雅的异步调用

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

        @Override        public void run() {
            System.out.println(Thread.currentThread().getName() + ":异步任务");
        }
    }

@Async优雅的异步调用

        //新建线程并执行任务类
        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

@Async优雅的异步调用

 

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

 

   代码与测试

  项目工程结构

@Async优雅的异步调用

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

@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>

@Async优雅的异步调用

 

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

@Async优雅的异步调用

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//开启异步调用@SpringBootApplicationpublic 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));
        };
    }
}

@Async优雅的异步调用

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

@Async优雅的异步调用

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);
}

@Async优雅的异步调用

@Async优雅的异步调用

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;

@Servicepublic 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("模拟异常");
        }
    }
}

@Async优雅的异步调用

 

  配置线程池

@Async优雅的异步调用

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;/**
 * 线程池的配置 */@Configurationpublic 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会默认从线程池获取线程,当然也可以显式的指定@Async("asyncTaskExecutor")

 

  无返回值

@Async优雅的异步调用

     applicationArguments -> startTime =+ ":开始调用异步业务"
             endTime =+ ":调用异步业务结束,耗时:" + (endTime -

@Async优雅的异步调用

 @Async优雅的异步调用

 

  有返回值

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

@Async优雅的异步调用

    /**
     * 启动成功     */
    @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));
        };
    }

@Async优雅的异步调用

 @Async优雅的异步调用

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

@Async优雅的异步调用

    /**
     * 启动成功     */
    @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));
        };
    }

@Async优雅的异步调用

@Async优雅的异步调用

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

 

  事务测试

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

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

 

  事务提交

@Async优雅的异步调用

    /**
     * 启动成功     */
    @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));
        };
    }

@Async优雅的异步调用

@Async优雅的异步调用

@Async优雅的异步调用

@Async优雅的异步调用

 

  模拟异常,事务回滚

@Async优雅的异步调用

    /**
     * 启动成功     */
    @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));
        };
    }

@Async优雅的异步调用

@Async优雅的异步调用

@Async优雅的异步调用

 @Async优雅的异步调用

 

  后记

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

 

  更新、补充

  2021-07-12更新

  除了使用@Async注解来开启异步任务,也可以使用线程池对象,来开启异步任务

@Async优雅的异步调用

    @Autowired
    AsyncTaskExecutor asyncTaskExecutor;//注入线程池对象    //通过线程池对象提交异步任务
    asyncTaskExecutor.submit(() -> {
        log.info("异步任务开始");        
        //省略异步任务业务逻辑...
        log.info("异步任务结束");
    });

@Async优雅的异步调用

 

  代码开源

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

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

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

 

版权声明

作者:huanzi-qch

出处:https://www.cnblogs.com/huanzi-qch

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: