阅读 682

SpringBoot技术实践-异步任务Async

一、异步任务启动

  1. 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理,在处理与第三方系统交互的时候,同步容易造成响应迟缓的情况
  2. 在Spring 3.x之后,就已经内置了 @Async 来完美解决这个问题,@Async为异步执行注解
    • 异步执行为直接返回null,或者方法不需要返回值
    • 所以需要注意异步方法的返回值需要能接收null,推荐无返回值,因为返回的也是null
  3. 两个重要注解
    • @EnableAysnc:启动类上开启异步模式
    • @Aysnc:需要异步处理的方法
  4. @Aysnc异步方法
    • 注意不能自调用,否则注解失效
  5. 创建异步执行方法
import com.codecoord.util.PrintUtil;
import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class AsyncHandler {

    @Async
    public Long handler(long batch) {
        PrintUtil.print("任务任务开始执行....批次号:" + batch);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        PrintUtil.print("任务任务执行结束....批次号:" + batch + ",完成时间:" + LocalDateTime.now());
        return batch;
    }
}
复制代码
  1. 创建异步执行controller
import com.codecoord.springboot.practice.async.handler.AsyncHandler;
import java.time.LocalDateTime;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncController {
    @Resource
    private AsyncHandler asyncHandler;

    @RequestMapping("/async")
    public Object async() {
        long batch = System.currentTimeMillis();
        return batch + " : " + asyncHandler.handler(batch) + ",完成时间:" + LocalDateTime.now();
    }
}
复制代码
  1. 启动类开启异步功能
import com.codecoord.InitialBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@InitialBootApplication
@EnableAsync
public class SpringbootAsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootAsyncApplication.class, args);
    }
}
复制代码
  1. 访问接口,可以看到有如下处理

接口返回时间

1619428692595 : null,完成时间:2021-04-26T17:18:12.603
复制代码

方法处理时间

task-1 - 任务任务开始执行....批次号:1619428692595
task-1 - 任务任务执行结束....批次号:1619428692595,完成时间:2021-04-26T17:18:15.614
复制代码
  1. 从执行结果可以看到,接口对于异步方法返回的是false,没有等待返回值;方法处理时间和接口返回是独立的,也就是收到请求之后就返回;方法执行的没有使用main线程,说明是单独开启线程推送的

二、异步线程池配置

  1. @Aysnc默认使用默认的线程处理,如果需要自定义则需要自定义配置
  2. 新建线程配置类,用于接收配置文件配置参数
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "async.task.pool")
public class AsyncTaskPoolProperties {
    /**
     * 核心线程池
     */
    private int corePoolSize = 5;
    /**
     * 最大线程池
     */
    private int maxPoolSize = 50;
    /**
     * 线程空闲时间
     */
    private int keepAliveSeconds = 60;
    /**
     * 队列长度
     */
    private int queueCapacity = 10000;
    /**
     * 线程名称前缀
     */
    private String threadNamePrefix = "TX-AsyncTask-";

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getKeepAliveSeconds() {
        return keepAliveSeconds;
    }

    public void setKeepAliveSeconds(int keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
    }

    public int getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

    public String getThreadNamePrefix() {
        return threadNamePrefix;
    }

    public void setThreadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
    }
}
复制代码
  1. 实现AsyncConfigurer接口,实现该接口后将会作为默认线程提供;也可以继承AsyncConfigurerSupport
    • getAsyncExecutor:配置线程执行器
    • getAsyncUncaughtExceptionHandler:异常处理方法
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import javax.annotation.Resource;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * 异动线程池配置
 */
@Configuration
public class AsyncTaskPoolConfig implements AsyncConfigurer {

    @Resource
    private AsyncTaskPoolProperties asyncTaskPoolProperties;

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(asyncTaskPoolProperties.getCorePoolSize());
        executor.setMaxPoolSize(asyncTaskPoolProperties.getMaxPoolSize());
        executor.setKeepAliveSeconds(asyncTaskPoolProperties.getKeepAliveSeconds());
        executor.setQueueCapacity(asyncTaskPoolProperties.getQueueCapacity());
        executor.setThreadNamePrefix(asyncTaskPoolProperties.getThreadNamePrefix());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setAllowCoreThreadTimeOut(true);
        // 线程池初始化
        executor.initialize();
        return executor;
    }

    /**
     * 异常处理
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
                System.out.println("throwable = " + throwable + ", method = " + method + ", objects = " + Arrays
                        .deepToString(objects));
            }
        };
    }
}
复制代码
  1. 可以在配置文件中执行配置,也可以使用默认
async:
  task:
    pool:
      corePoolSize: 10
      maxPoolSize: 1000
复制代码
  1. 重新启动项目,然后访问,可以看到线程名称将会变成定义的名称
19:22:21.214 - TX-AsyncTask-1 - 任务任务开始执行....批次号:1619436141201
19:22:21.752 - TX-AsyncTask-2 - 任务任务开始执行....批次号:1619436141752
19:22:22.288 - TX-AsyncTask-3 - 任务任务开始执行....批次号:1619436142288
19:22:22.691 - TX-AsyncTask-4 - 任务任务开始执行....批次号:1619436142691
19:22:23.101 - TX-AsyncTask-5 - 任务任务开始执行....批次号:1619436143101
19:22:24.219 - TX-AsyncTask-1 - 任务任务执行结束....批次号:1619436141201,完成时间:2021-04-26T19:22:24.219
19:22:24.759 - TX-AsyncTask-2 - 任务任务执行结束....批次号:1619436141752,完成时间:2021-04-26T19:22:24.759
19:22:25.300 - TX-AsyncTask-3 - 任务任务执行结束....批次号:1619436142288,完成时间:2021-04-26T19:22:25.300
19:22:25.705 - TX-AsyncTask-4 - 任务任务执行结束....批次号:1619436142691,完成时间:2021-04-26T19:22:25.705
19:22:26.111 - TX-AsyncTask-5 - 任务任务执行结束....批次号:1619436143101,完成时间:2021-04-26T19:22:26.111
复制代码
文章分类
后端