在上一篇文章中,我们介绍了如何使用@Async注解来创建异步任务。我可以用这个方法实现一些并发操作来加快任务的执行效率。但是,如果只是像以前一样直接简单地创建和使用,可能还是会遇到一些问题。问题是什么?我们先考虑一下。通过异步任务加速执行下面的接口实现有没有问题或者风险?@RestControllerpublicclassHelloController{@AutowiredprivateAsyncTasksasyncTasks;@GetMapping("/hello")publicStringhello(){//将并行处理逻辑拆分为三个异步任务执行CompletableFuturetask1=asyncTasks.doTaskOne();CompletableFuturetask2=asyncTasks.doTaskTwo();CompletableFuturetask3=asyncTasks.doTaskThree();CompletableFuture.allOf(task1,task2,task3).join();返回“你好世界”;}}虽然,从单一的接口调用来说,是没有问题的。但是当接口被客户端频繁调用时,异步任务的数量会明显增加:3xn(n为请求数)。如果任务处理速度不够快,可能会发生内存溢出。那么为什么会内存溢出呢?根本原因是SpringBoot默认的异步任务线程池是这样配置的:图中我标注的两个重要参数需要注意:queueCapacity:缓冲队列的容量,默认为INT(2)的最大值31次幂-1)。maxSize:允许的最大线程数,默认为INT的最大值(2的31次方-1)。因此,默认情况下,通用任务队列可能会填满内存。所以我们在实际使用的时候,需要对异步任务的执行线程池进行一些基础配置,防止内存溢出导致服务不可用。配置默认线程池默认线程池的配置非常简单,只需要在配置文件中完成即可。主要有以下几个参数:spring.task.execution.pool.core-size=2spring.task.execution.pool.max-size=5spring.task.execution.pool.queue-capacity=10spring.task.execution.pool.keep-alive=60sspring.task.execution.pool.allow-core-thread-timeout=truespring.task.execution.shutdown。await-termination=falsespring.task.execution.shutdown.await-termination-period=spring.task.execution.thread-name-prefix=task-specific配置含义如下:spring.task.execution.pool.core-size:thread创建池时初始化线程的个数,默认为8task的队列,默认为int的最大值spring.task.execution.pool.keep-alive:允许保持空闲的时间线程终止前spring.task.execution.shutdown.await-termination:是否等待剩余任务完成后关闭应用程序spring.task.execution.shutdown.await-termination-period:最大等待remainingtaskstocompletespring.task.execution.thread-name-prefix:线程名的前缀。设置之后,方便我们在日志中查看处理任务所在的线程池。试试看。我们将根据前面chapter7-5的结果直接进行下面的操作。首先,在配置线程池之前,可以进行单元测试:@Testpublicvoidtest1()throwsException{longstart=System.currentTimeMillis();CompletableFuturetask1=asyncTasks.doTaskOne();CompletableFuturetask2=asyncTasks.doTaskTwo();CompletableFuturetask3=asyncTasks.doTaskThree();CompletableFuture.allOf(task1,task2,task3).join();长端=System.currentTimeMillis();log.info("任务全部完成,总耗时为:"+(end-start)+"milliseconds");}由于默认线程池的核心线程数为8,所以会启动3个任务同时执行,日志输出如下:2021-09-1500:30:14.819INFO77614---[task-2]com.didispace.chapter76.AsyncTasks:开始做任务二2021-09-1500:30:14.819INFO77614---[task-3]com.didispace.chapter76.AsyncTasks:启动任务32021-09-1500:30:14.819INFO77614---[task-1]com.didispace.chapter76.AsyncTasks:starttask12021-09-1500:30:15.491INFO77614---[task-2]com.didispace.chapter76.AsyncTasks:Completetask2,耗时:672毫秒2021-09-15点30分:19.496INFO77614---[task-3]com.didispace.chapter76.AsyncTasks:完成任务三,耗时:4677毫秒2021-09-1500:30:20.443INFO77614---[task-1]com.didispace.chapter76.AsyncTasks:完成任务一,耗时:5624毫秒2021-09-1500:30:20.443INFO77614---[main]c.d.chapter76.Chapter76ApplicationTests:所有任务完成,总耗时:5653milliseconds接下来,可以尝试添加如下线程池配置spring.task.execution.pool.core-size=2spring.task.execution.pool.max-size=5spring.task.execution.pool.queue-capacity=10spring在配置文件中。task.execution.pool.keep-alive=60sspring.task.execution.pool.allow-core-thread-timeout=truespring.task.execution.thread-name-prefix=task-日志输出的顺序会变成下面的顺序:2021-09-1500:31:50.013INFO77985---[task-1]com.didispace.chapter76.AsyncTasks:开始执行任务一2021-09-1500:31:50.013INFO77985---[任务-2]com.didispace.chapter76.AsyncTasks:Starttask22021-09-1500:31:52.452INFO77985---[task-1]com.didispace.chapter76.AsyncTasks:完成任务1,耗时:2个439毫秒2021-09-1500:31:52.452INFO77985---[task-1]com.didispace.chapter76.AsyncTasks:开始执行任务三2021-09-1500:31:55.880INFO77985---[task-2]com.didispace.chapter76.AsyncTasks:完成任务2,耗时:5867毫秒2021-09-1500:32:00.346INFO77985---[task-1]com.didispace.chapter76.AsyncTasks:完成任务3,耗时:7894毫秒2021-09-1500:32:00.347INFO77985---[main]c.d.chapter76.Chapter76ApplicationTests:所有任务完成,总耗时:10363毫秒任务1和任务2会立即占用核心线程,任务3进入队列等待任务1完成,释放一个核心线程,任务3从队列中移除,占用核心线程开始处理注意:这里有朋友可能会问,不是最大线程5,为什么task3是进入缓冲队列,而不是创建一个新的线程来处理呢?这里我们需要了解一下bufferqueue和最大线程的关系:只有bufferqueue满了之后,才会申请超过核心线程数的线程进行处理。因此,这里只有缓冲队列中的10个任务满了,然后第11个任务才会在线程池中创建第三个线程进行处理。这里就不专门写了。读者可以自行调整参数,或者调整单元测试来验证这个逻辑。本系列教程《Spring Boot 2.x基础教程》直接点击!,欢迎收藏转发!如果你在学习过程中遇到困难?大家可以加入我们的Spring技术交流群,参与交流讨论,更好的学习进步!代码示例本文的完整工程可以在下面仓库2.x目录下的chapter7-6工程中查看:Github:https://github.com/dyc87112/SpringBoot-Learning/Gitee:https://gitee.com/didispace/SpringBoot-Learning/如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!欢迎关注我的公众号:程序员DD,分享别处看不到的知识和思考