• Spring Boot 到底是单线程还是多线程


    前言

    我们知道用Spring Boot来构建项目非常的方便,开发过程中我们会使用它来快速的构建项目,自带tomcat容器、启动也会将需要配置的bean全部加载到我们的Spring容器里面。但是在调用Controller方法的时候,是单线程、还是多线程的呢,今天我们来一起研究下。

    执行Controller

    首先我们通过执行两个Controller方法,发现打印的日志,执行这两个方法分别是不同的线程,但是对创建的变量进行了叠加,因为Spring的默认是以单例模式创建的,所以就会导致线程不安全。

    我们在Çontroller头部加入@Scope,使它的作用域变成原型模式,这时候我们再次执行方法。

    1. @RequestMapping(“test”)
    2. @Scope(“prototype”)
    3. @RestController
    4. public class TestController {
    5. 复制代码

    可以看到依旧是两个线程执行,但是我们变量的操作没有进行类加,而是分别进行了i++的操作,证明每次的reqeust请求都会创建一个bean。 注意⚠️:我们写代码的时候千万不能在Çontroller里面定义常量,然后方法里面进行操作,会有很大的问题,这里只是做演示。

    由此可见Spring Boot中Controller默认是单例模式,并且执行的时候是多线程的方式。

    @Async

    既然提到了这里,我们就来说下Spring Boot的一个多线程首先原理,先来看下我们的异步执行注解@Async,通过使用注解的方式,在我们需要异步执行方法的场景下,实现一个异步执行。 当然也离不开@EnableAsync这个注解,它是一个异步注解的开关。可以看到使用的是代理的模式。

    通过看源码追踪到AsyncTaskExecutor 这个接口,看名字就知道是异步任务的一个线程池创建的接口,继承的TaskExecutor父接口。

    可以看到ThreadPoolTaskExecutor这个线程池的配置类,可以看到异步操作其实是由创建线程池来完成的,通过向线程池提交task任务,用Future类接收,可以获取异步任务的执行结果。

    使用Async

    下面来通过自定线程池,然后先线程池提交我们的异步任务。

    AsyncService异步方法

    1. @Service
    2. public class AsyncService {
    3. private Logger logger = LoggerFactory./getLogger/(TestController.class);
    4. @Async(“taskExecutor”)
    5. public String getAsyncMessage(String message) {
    6. try {
    7. logger.info(“执行异步方法,message is “ + message);
    8. Thread./sleep/(1000);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. return message;
    13. }
    14. 复制代码

    AsyncConfig异步线程池配置

    1. public class AsyncConfig {
    2. @Bean(“taskExecutor”)
    3. public Executor taskExecutor(){
    4. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    5. // 核心线程数:线程池创建时候初始化的线程数
    6. executor.setCorePoolSize(10);
    7. // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
    8. executor.setMaxPoolSize(20);
    9. // 缓冲队列:用来缓冲执行任务的队列
    10. executor.setQueueCapacity(500);
    11. // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
    12. executor.setKeepAliveSeconds(60);
    13. // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
    14. executor.setThreadNamePrefix(“Async-task-“);
    15. // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
    16. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    17. executor.initialize();
    18. return executor;
    19. }
    20. 复制代码

    执行结果,我们可以看见异步方法通过我们定义的线程池提交,没有按照for循环的顺序提交,而是实现了异步的提交。

    总结

    Spring Boot框架是一个可以实现多线程的框架,不是一个线程安全的框架,我们要使它变成线程安全,还需要集成其他组件,包括用到一些线程安全的类,但是可以说明Spring Boot在执行Controller方法的时候确实是以多线程的方式执行的,包括异步的方法。

     

  • 相关阅读:
    Python图像处理算法实战【1】超详细整理 | 新手入门实用指南 | 图像处理基础
    大数据----数据仓库架构
    ES6语法学习
    C语言:栈和队列+leetcode232、leetcode225:队实现栈、栈实现队列、leetcode20括号匹配
    vue elementui 自定义loading显示的icon 文本 背景颜色
    浅谈云原生Cloud Native
    基于springboot在线考试报名系统毕业设计源码031706
    .NET周报【11月第2期 2022-11-15】
    Hadoop-19 Flume Agent批量采集数据到HDFS集群 监听Hive的日志 操作则把记录写入到HDFS 方便后续分析
    【golang】go app 优雅关机 Graceful Shutdown How?
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/126194275