【问题标题】:Spring Boot: Can we have seperate thread pool for each request?Spring Boot:我们可以为每个请求设置单独的线程池吗?
【发布时间】:2020-04-22 15:19:12
【问题描述】:

我已经使用 Spring Boot @Async 在我的应用程序中实现了一个方法的异步执行。我有一个带有 20 个线程的自定义线程池。在 for 循环中调用 async 方法 30 次。

每个单独的请求都是异步执行的,但是当我从浏览器同时向我的 API 发出两个不同的请求时,第一个请求正在执行,然后是第二个请求。不是两个请求并行执行相同的方法。

我认为当第一个请求到达应用程序时,它开始执行异步方法,并且由于它正在执行 30 次并且我的池有 20 个线程,所有线程都忙于执行第一个请求。所以即使是第二个请求也因为线程池繁忙而执行,另一个请求正在等待池中的线程空闲。

我们可以为每个单独的请求设置单独的线程池吗?或者我们可以使每个请求的执行独立于其他请求处理的任何方式。

这是我的代码示例。

@SpringBootApplication
@EnableAsync
public class AppBootStrap
{
  public static void main(String[] args)
  {
    SpringApplication.run(AppBootStrap.class, args);
  }

  @Bean
  public AsyncTaskService asyncTaskService() {
    return new AsyncTaskService();
  }

  @Bean(name="customExecutor")
  public Executor taskExecutor() {
    ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
    poolExecutor.setCorePoolSize(10);
    poolExecutor.setMaxPoolSize(20);
    poolExecutor.setThreadNamePrefix("customPoolExecutor");
    poolExecutor.initialize();
    return poolExecutor;
  }
}

**Controller Class:**

@RestController
public class TaskController
{
  @Autowired
  private TaskService taskService;

 @RequestMapping("/performAction")
 public void performAction() {
    taskService.performAction();
 }
}

**Service class**

@Service
public class TaskService
{
  @Autowired
  private AsyncTaskService asyncTaskService;

  public void performAction()
  {
    for(int i = 0; i < 30; i++) {
        asyncTaskService.greetings(i);
    }
  }
}

**Async Method Class**

public class AsyncTaskService
{
  @Async
  public void greetings(Integer i)
  {
    try
    {
        Thread.sleep(500 * (i + 10));
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
    System.out.println("Welcome to Async Service: " + i);
  }
}

【问题讨论】:

  • Not both requests executing the same method in parallel. 这完全是错误的,在 servlet 容器中,每个请求都有自己的线程,因此所有内容都始终按照定义并行。但是如果没有剩余线程。其他请求需要等待,但每个请求总是并行执行。
  • 如果你想使用你的自定义执行器,你应该在异步注释中提供它的名称@Async("customExecutor")
  • 你的话完全正确。但是在这种情况下,两个差异请求到达服务器,所以执行肯定是并行的,但是当执行到这个特定的逻辑(异步方法执行)时,就会发生等待。对于异步注释,我还添加了自定义执行器。忘记在上面的代码中提及了。
  • 请更新您的代码,然后使用正确的代码

标签: java multithreading spring-boot threadpoolexecutor


【解决方案1】:
    @Bean(name = "apiTaskExecutor")
    public ThreadPoolTaskExecutor apiTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(20);
    executor.setMaxPoolSize(100);
    executor.setQueueCapacity(50);
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

@Bean(name = "lruTaskExecutor")
    public ThreadPoolTaskExecutor lruTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(100);
    executor.setMaxPoolSize(200);
    executor.setQueueCapacity(500);
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

这样做的方法是使用不同的限定符名称创建两个不同的线程池。 (如上图)

    @Autowired
    @Qualifier("apiTaskExecutor")
    private ThreadPoolTaskExecutor apiTaskExecutor;

然后使用您给定的限定符自动装配所需的池。您还可以使用 @Async 注释而不是 autowiring 。我更喜欢这种方式。

如果我没记错的话,这种为不同任务使用不同线程池的方式称为隔板模式。

【讨论】:

    【解决方案2】:

    我认为问题在于 customExecutor bean 是一个单例。这意味着当第二个请求尝试使用这个 bean 时,它会发现 thred 池的所有线程都忙。您可以尝试使用 @Scope("prototype") 注释使 customExecutor bean 非单例,这将导致在请求此类型的 bean 时实例化一个新的。

    【讨论】:

    • @Bean(name = "customExecutor") @Scope("prototype") public Executor taskExecutor() { ThreadPoolTask​​Executor poolExecutor = new ThreadPoolTask​​Executor(); poolExecutor.setCorePoolSize(10); poolExecutor.setMaxPoolSize(20); poolExecutor.setThreadNamePrefix("customPoolExecutor"); poolExecutor.initialize();返回池执行器; }
    猜你喜欢
    • 1970-01-01
    • 2015-08-09
    • 2015-07-11
    • 2018-05-22
    • 1970-01-01
    • 2018-02-25
    • 2016-05-23
    • 2011-08-21
    • 1970-01-01
    相关资源
    最近更新 更多