Springboot——自定义线程池并使用

关于线程池

不熟悉线程池的玩法,可以先看我之前的几篇博客,了解JUC中的线程池的定义,各个参数含义,以及玩法。

JUC之线程池

JUC之线程池七大参数理解

JUC之线程池问题补充

Spring自定义线程池

既然放入Spring中,依靠Spring来管理线程池的生命周期。当然也需要注意使用单例,如果使用多例,和直接new Thread又有什么区别。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定义线程池配置
 * 需要使用线程池  则开启当前配置
 */
@Configuration
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolExecutor myThreadPool(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,//核心数,一直都能工作的数量
                5,//请求处理大时,可以开放的最大工作数
                3,//开启最大工作数后,当无请求时,还让其存活的时间
                TimeUnit.SECONDS,//存活时间单位
                new LinkedBlockingDeque<>(10),//阻塞队列,保存操作请求线程
                Executors.defaultThreadFactory(),//创建线程的工厂类
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略
        return threadPoolExecutor;
    }
}

具体使用,可以看下列案例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@RestController
public class TestController {

    // 注入自定义线程池bean
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @RequestMapping("/getThreadPoolInfo")
    public void getThreadPoolInfo(){
        System.out.println("getActiveCount--"+threadPoolExecutor.getActiveCount());
        System.out.println("getCorePoolSize--"+threadPoolExecutor.getCorePoolSize());
        System.out.println("getPoolSize--"+threadPoolExecutor.getPoolSize());
    }

     @RequestMapping("/test1")
    public String test1(){
        System.out.println("进入test1 "+System.currentTimeMillis());
        threadPoolExecutor.execute(()->{
            try {
                System.out.println("线程池处理数据  start "+System.currentTimeMillis());
                TimeUnit.SECONDS.sleep(3);
                System.out.println("线程池处理数据  end "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("线程池调用结束 "+System.currentTimeMillis());
        return "6666";
    }
}

请求测试

http://localhost:8080/test1

在这里插入图片描述
从执行顺序和执行时间上可以很清楚的发现:

自定义线程池可以调用并异步处理数据。

2021.09.16 新的方式

大家有没有想过一个问题,SpringBoot中就没有提供更佳简单的方式,让开发者更轻松的调用自定义的线程池么?

接下来一起看看新的方式。

1、修改自定义线程池的配置bean文件。
原来的bean配置如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定义线程池bean配置
 */
@Configuration
public class ThreadPoolConfig {
    @Bean
    public ThreadPoolExecutor myThreadPool(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,//核心数,一直都能工作的数量
                5,//请求处理大时,可以开放的最大工作数
                3,//开启最大工作数后,当无请求时,还让其存活的时间
                TimeUnit.SECONDS,//存活时间单位
                new LinkedBlockingDeque<>(10),//阻塞队列,保存操作请求线程
                Executors.defaultThreadFactory(),//创建线程的工厂类
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略
        return threadPoolExecutor;
    }
}

这次呢,我们将类上添加注解@EnableAsync 开启异步调用,让在需要使用的地方,直接使用@Async就可以了。

这样就能省略掉 threadPoolExecutor.execute

修改后的bean如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定义线程池bean配置
 */
@Configuration
@EnableAsync // 开启 Async
public class ThreadPoolConfig {
    
    @Bean("myThreadPoolExecutor")
    public ThreadPoolExecutor myThreadPool(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,//核心数,一直都能工作的数量
                5,//请求处理大时,可以开放的最大工作数
                3,//开启最大工作数后,当无请求时,还让其存活的时间
                TimeUnit.SECONDS,//存活时间单位
                new LinkedBlockingDeque<>(10),//阻塞队列,保存操作请求线程
                Executors.defaultThreadFactory(),//创建线程的工厂类
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略
        return threadPoolExecutor;
    }
}

@Bean注解中添加一个名称。

如果没有配置名称,spring加载bean是按照方法名首字母小写方式命名

然后编写一个测试类,同时也需要将具体的异步业务单独独立出来:

 @Autowired
 private MyService myservice;

 @RequestMapping("/test2")
 public void test2() throws InterruptedException {
     System.out.println("进入test2 "+System.currentTimeMillis());
     myservice.service1();
     System.out.println("调用结束 "+System.currentTimeMillis());
 }

服务类的代码如下所示:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class MyService {

    // 具体使用哪个线程池
    @Async("myThreadPoolExecutor")
    public void service1() throws InterruptedException {
        System.out.println("----- service1 start --------"+System.currentTimeMillis());
        TimeUnit.SECONDS.sleep(3);
        System.out.println("----- service1 end --------"+System.currentTimeMillis());
    }
}

执行请求:

http://localhost:8080/test2

日志信息如下:
在这里插入图片描述
发现:

@Async(“myThreadPoolExecutor”)

的使用,是将整个service1()进行了异步执行,相当于如下所示的代码:

threadPoolExecutor.execute(()->{
   service1()
});

参考资料

@EnableAsync@Async使用总结

SpringBoot 线程池

测试demo链接

https://gitee.com/xiangjiaobunana/springboot-thread-pool

### 如何在 Spring Boot 中实现和配置虚拟线程 #### 配置虚拟线程执行器 为了使应用程序能够利用虚拟线程,在 `Spring Boot` 应用程序中可以创建自定义的 `ExecutorService` 来替代默认的任务执行者。这可以通过定义一个带有 `@Configuration` 注解的类来完成,该类提供了一个返回 `Executors.newVirtualThreadPerTaskExecutor()` 方法调用的结果作为 bean 的方法[^4]。 ```java @Configuration public class VirtualThreadConfig { @Bean public ExecutorService virtualThreadExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); } } ``` 上述代码片段展示了如何设置用于启动新任务的虚拟线程池。每当提交一个新的任务给这个执行服务时,都会有一个新的虚拟线程被创建出来处理此任务。 #### 修改应用属性文件以支持虚拟线程 为了让基于Tomcat的嵌入式服务器也采用虚拟线程模型工作,可以在项目的 `application.properties` 或 `application.yml` 文件里指定特定参数。对于使用 `.properties` 格式的配置来说: ```properties server.tomcat.threads.max=200 spring.task.execution.pool.max-size=1000 server.tomcat.threadpool-type=virtual ``` 而对于 YAML 用户,则应如下所示调整设置: ```yaml server: tomcat: threads: max: 200 threadpool-type: virtual spring: task: execution: pool: max-size: 1000 ``` 这些更改使得 Tomcat 可以更好地适应大量发请求,允许其内部操作充分利用 JDK 提供的新功能——即虚拟线程的支持[^5]。 #### 编写异步控制器方法 一旦完成了必要的基础架构准备工作之后,就可以编写相应的 RESTful API 接口了。下面是一个简单的例子,说明怎样在一个 HTTP GET 请求处理器里面运用前面提到过的虚拟线程执行器来进行长时间运行的操作而不会阻塞主线程。 ```java @RestController @RequestMapping("/api/vt") public class AsyncController { private final ExecutorService executor; @Autowired public AsyncController(ExecutorService executor) { this.executor = executor; } @GetMapping("/long-task") CompletableFuture<String> longRunningTask() throws InterruptedException { return CompletableFuture.supplyAsync(() -> { try { Thread.sleep(Duration.ofSeconds(5).toMillis()); // Simulate a time-consuming operation. return "Operation completed!"; } catch (InterruptedException e) { throw new RuntimeException(e); } }, executor); } } ``` 这段代码中的 `/api/vt/long-task` 终端会触发一次模拟耗时五秒的任务,但它不会占用宝贵的平台线程资源;相反,它会被分配到由之前定义好的 `virtualThreadExecutor()` 函数所提供的轻量级单元上去执行[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值