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

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。
    控制台打印接口如图:
    控制台打印结果
    代码中将异步线程进行了睡眠,但是接口可以做到秒返回(如果你的项目没有问题的话),说明异步线程处理成功了。当异步线程不需要返回的时候,就可以用这个来做。
    1. 画图比较

在图中,如果任务二与这个请求中其他任务没有关系,那么使用这种异步方式就可以很好的减少请求花费的时间了 同步请求和异步请求的比较

练习使用对象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。
    在这里插入图片描述
  2. 线程池对象分析
    上一步有同学可能会疑惑AsyncTaskExecutor和ThreadPoolTaskExecutor的关系,这里咱们可以看一下ThreadPoolTaskExecutor的集成关系。不多说,上图:
    这是ThreadPoolTaskExecutor类,有没有觉得AsyncListenableTaskExecutor很眼熟ThreadPoolTaskExecutor
    我们继续来看一下AsyncListenableTaskExecutor对象,可以看到AsyncListenableTaskExecutor是继承了AsyncTaskExecutor类的
    AsyncListenableTaskExecutor
    从这两张图我们就可以看出AsyncTaskExecutor和ThreadPoolTaskExecutor的关系了。
    这里再贴上两张图,一个是ThreadPoolTaskExecutor的继承关系,一个是ThreadPoolTaskScheduler的继承关系,这两个对象基本差不多,不过我暂时还没有研究ThreadPoolTaskScheduler对象,所以对这两个对象的了解程度也不高。貌似ThreadPoolTaskScheduler是用来做定时任务的,有了解的可以交流一下哈ThreadPoolTaskExecutor
    ThreadPoolTaskScheduler
  3. 下班了,不多说了,运行结果同学们可以自己实验一下,这里给个结论
    web服务器中,每个请求会启动一个处理线程,由线程池管理(不是我们前面配置的自定义线程池)。
    在普通的场景下,如果服务器负载不大,并且后端服务也给力,同步调用并没有什么问题。
    但在高并发场景下,请求服务端的线程总数是有限的,如果某个线程一直处于阻塞状态,就会影响系统的吞吐量。所以我们可以在请求进来后,将处理线程释放,让他可以去处理别的请求,而当前请求我们可以交给自定义线程池去处理。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值