荐 web服务的多线程下异步调用的两种方式@Asynz和WebAsyncTask对象
程序员文章站
2022-04-19 09:08:53
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
- 创建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");
}
}
- 创建一个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());
}
}
- 结果
调用get请求 (ip+端口)/thread。
控制台打印接口如图:
代码中将异步线程进行了睡眠,但是接口可以做到秒返回(如果你的项目没有问题的话),说明异步线程处理成功了。当异步线程不需要返回的时候,就可以用这个来做。- 画图比较
在图中,如果任务二与这个请求中其他任务没有关系,那么使用这种异步方式就可以很好的减少请求花费的时间了
练习使用对象WebAsyncTask
- 在前面的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;
}
- WebAsyncTask对象分析
我们点进WebAsyncTask对象,我们可以看到它提供了集中不同的构造器。
重点说第四个。
提供了一个由超时时间,线程池,callable对象创建的构造器。在前面线程池配置的时候我提过,需要将线程池对象交给spring容器管理,所以在这里我们只要用注解@Autowired引入进来就可以了。
注意:构造方法中传入的线程池对象为AsyncTaskExecutor,而线程池配置中配置的是ThreadPoolTaskExecutor。
- 线程池对象分析
上一步有同学可能会疑惑AsyncTaskExecutor和ThreadPoolTaskExecutor的关系,这里咱们可以看一下ThreadPoolTaskExecutor的集成关系。不多说,上图:
这是ThreadPoolTaskExecutor类,有没有觉得AsyncListenableTaskExecutor很眼熟
我们继续来看一下AsyncListenableTaskExecutor对象,可以看到AsyncListenableTaskExecutor是继承了AsyncTaskExecutor类的
从这两张图我们就可以看出AsyncTaskExecutor和ThreadPoolTaskExecutor的关系了。
这里再贴上两张图,一个是ThreadPoolTaskExecutor的继承关系,一个是ThreadPoolTaskScheduler的继承关系,这两个对象基本差不多,不过我暂时还没有研究ThreadPoolTaskScheduler对象,所以对这两个对象的了解程度也不高。貌似ThreadPoolTaskScheduler是用来做定时任务的,有了解的可以交流一下哈
- 下班了,不多说了,运行结果同学们可以自己实验一下,这里给个结论
web服务器中,每个请求会启动一个处理线程,由线程池管理(不是我们前面配置的自定义线程池)。
在普通的场景下,如果服务器负载不大,并且后端服务也给力,同步调用并没有什么问题。
但在高并发场景下,请求服务端的线程总数是有限的,如果某个线程一直处于阻塞状态,就会影响系统的吞吐量。所以我们可以在请求进来后,将处理线程释放,让他可以去处理别的请求,而当前请求我们可以交给自定义线程池去处理。
本文地址:https://blog.csdn.net/qq_37271924/article/details/107253411