欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象

程序员文章站 2024-01-14 12:36:40
web服务的多线程下异步调用@Asynz和WebAsyncTask对象前言介绍项目环境准备线程池准备练习注解@Asynz练习使用对象WebAsyncTask前言介绍本文介绍异步线程调用的两种方式:注解实现和WebAsyncTask对象。项目环境准备基于spring boot快速搭建的一个项目线程池准备线程池的好处就不多说了,不过如果不想创建线程池的话也可以继续下去,直接跳过这步就行。新建一个配置文件,直接复制下面的就行,具体参数都有写注释,可以根据自己的需要调整。注解@Configurati...

web服务的多线程下异步调用@Asynz和WebAsyncTask对象

前言介绍

本文介绍异步线程调用的两种方式:注解实现和WebAsyncTask对象。

项目环境准备

基于spring boot快速搭建的一个项目

线程池准备

线程池的好处就不多说了,不过如果不想创建线程池的话也可以继续下去,直接跳过这步就行。
新建一个配置文件,直接复制下面的就行,具体参数都有写注释,可以根据自己的需要调整。
注解@Configuration就不多说了;
注解@EnableAsync是开启异步,开启异步后才能完美使用@Asynz进行异步调用;
注解 @Bean是将线程池对象交给spring容器管理,在创建WebAsyncTask对象的时候需要传入一个线程池对象,关于WebAsyncTask对象在后面介绍

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池配置类
 * tasks :每秒的任务数
 * taskcost:每个任务花费时间,假设为0.1s
 * responsetime:系统允许容忍的最大响应时间,假设为1s
 *
 */
@Configuration
@EnableAsync // 开启异步
public class ThreadPoolConfig {

    /**
     *   每秒需要多少个线程处理?
     */
    private int corePoolSize = 3;

    /**
     * 线程池维护线程的最大数量
     */
    private int maxPoolSize = 3;

    /**
     * 缓存队列
     */
    private int queueCapacity = 10;

    /**
     * 允许的空闲时间
     * 默认为60
     */
    private int keepAlive = 100;

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        // 设置队列容量
        executor.setQueueCapacity(queueCapacity);
        // 设置允许的空闲时间(秒)
		// executor.setKeepAliveSeconds(keepAlive);
        // 设置默认线程名称
        executor.setThreadNamePrefix("thread-");
        // 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

}

练习注解@Asynz

  1. 创建service接口并实现

接口

/**
 * 线程池测试
 */
public interface AsyncService {

    void executeAsync();
}

实现类
@Async放在方法上表示这个方法要异步调用,放在类上面,表示这个类的所有方法可以异步调用

/**
 1. 线程池测试代码
 */
@Service
public class AsyncServiceImpl implements AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

    @Override
    @Async
    public void executeAsync() {
        logger.info("start executeAsync");
        try {
            Thread.currentThread().sleep(10000);
            System.out.println("当前运行的异步线程名称:" + Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.info("end executeAsync");
    }
}
  1. 创建一个controller
@RestController
public class ThreadController {

    @Autowired
    private AsyncService asyncService;
    @Autowired
    private TaskExecutor taskExecutor;

    /**
     * 测试 异步注解:@Asynz
     *
     * @return
     */
    @GetMapping("/thread")
    public IResult thread() {
        //调用service层的任务
        asyncService.executeAsync();

        System.out.println("当前运行的处理线程名称:" + Thread.currentThread().getName());
        return JsonResult.success(Thread.currentThread().getName());
    }
}
  1. 结果
    调用get请求 (ip+端口)/thread。
    控制台打印接口如图:
    荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
    代码中将异步线程进行了睡眠,但是接口可以做到秒返回(如果你的项目没有问题的话),说明异步线程处理成功了。当异步线程不需要返回的时候,就可以用这个来做。
    1. 画图比较

在图中,如果任务二与这个请求中其他任务没有关系,那么使用这种异步方式就可以很好的减少请求花费的时间了 荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象

练习使用对象WebAsyncTask

  1. 在前面的controller中添加一个方法,方法中创建WebAsyncTask对象

	// 引入线程池对象
	@Autowired
    private TaskExecutor taskExecutor;
	 /**
     * 测试 异步任务:WebAsyncTask
     *
     * @return
     */
    @GetMapping("/threadTestWebAsyncTask")
    public  WebAsyncTask<IResult> threadTestWebAsyncTask() {

        WebAsyncTask<IResult> task = new WebAsyncTask<IResult>(1 * 1000L, (ThreadPoolTaskExecutor) taskExecutor, () -> 		   	{ 
            System.err.println("异步线程开始:" + Thread.currentThread().getName());
            Thread.sleep(3 * 1000L);
            System.err.println("异步线程:" + Thread.currentThread().getName());
            return JsonResult.success("task执行完成!");
        });
		// 超时会走这
        task.onTimeout(() -> {
            return JsonResult.error("task执行超时!!!!!");
        });
        // 异常会走这
        task.onError(() -> {
            return JsonResult.error("task执行异常!!!!!");
        });
        // 任务执行完成时调用该方法
        task.onCompletion(() -> {
            System.err.println("task执行完成啦!");
        });

        System.out.println("当前运行的处理线程名称:" + Thread.currentThread().getName());
        return task;
    }
  1. WebAsyncTask对象分析
    我们点进WebAsyncTask对象,我们可以看到它提供了集中不同的构造器。
    重点说第四个。
    提供了一个由超时时间,线程池,callable对象创建的构造器。在前面线程池配置的时候我提过,需要将线程池对象交给spring容器管理,所以在这里我们只要用注解@Autowired引入进来就可以了。
    注意:构造方法中传入的线程池对象为AsyncTaskExecutor,而线程池配置中配置的是ThreadPoolTaskExecutor。
    荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
  2. 线程池对象分析
    上一步有同学可能会疑惑AsyncTaskExecutor和ThreadPoolTaskExecutor的关系,这里咱们可以看一下ThreadPoolTaskExecutor的集成关系。不多说,上图:
    这是ThreadPoolTaskExecutor类,有没有觉得AsyncListenableTaskExecutor很眼熟荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
    我们继续来看一下AsyncListenableTaskExecutor对象,可以看到AsyncListenableTaskExecutor是继承了AsyncTaskExecutor类的
    荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
    从这两张图我们就可以看出AsyncTaskExecutor和ThreadPoolTaskExecutor的关系了。
    这里再贴上两张图,一个是ThreadPoolTaskExecutor的继承关系,一个是ThreadPoolTaskScheduler的继承关系,这两个对象基本差不多,不过我暂时还没有研究ThreadPoolTaskScheduler对象,所以对这两个对象的了解程度也不高。貌似ThreadPoolTaskScheduler是用来做定时任务的,有了解的可以交流一下哈荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
    荐
                                                        web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
  3. 下班了,不多说了,运行结果同学们可以自己实验一下,这里给个结论
    web服务器中,每个请求会启动一个处理线程,由线程池管理(不是我们前面配置的自定义线程池)。
    在普通的场景下,如果服务器负载不大,并且后端服务也给力,同步调用并没有什么问题。
    但在高并发场景下,请求服务端的线程总数是有限的,如果某个线程一直处于阻塞状态,就会影响系统的吞吐量。所以我们可以在请求进来后,将处理线程释放,让他可以去处理别的请求,而当前请求我们可以交给自定义线程池去处理。

本文地址:https://blog.csdn.net/qq_37271924/article/details/107253411