Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么使用的线程数高于要求?_Java_Multithreading_Spring Boot_Threadpoolexecutor - Fatal编程技术网

Java 为什么使用的线程数高于要求?

Java 为什么使用的线程数高于要求?,java,multithreading,spring-boot,threadpoolexecutor,Java,Multithreading,Spring Boot,Threadpoolexecutor,我有一个SpringBoot应用程序,允许最多45个并发请求。 现在,1请求在其旅程中使用threadPool A并行调用16个外部服务。因此,考虑到平均情况和最坏情况,我为它保留了以下配置: ThreadPoolTaskExecutor A = new ThreadPoolTaskExecutor(); A.setCorePoolSize(400); A.setMaxPoolSize(1000); A.setQueueCapacity(10); A.setThreadNamePrefix("a

我有一个SpringBoot应用程序,允许最多45个并发请求。 现在,1请求在其旅程中使用
threadPool A并行调用16个外部服务。因此,考虑到平均情况和最坏情况,我为它保留了以下配置:

ThreadPoolTaskExecutor A = new ThreadPoolTaskExecutor();
A.setCorePoolSize(400);
A.setMaxPoolSize(1000);
A.setQueueCapacity(10);
A.setThreadNamePrefix("async-executor");
A.initialize();
我的期望是最多使用45*16=720个线程。但在运行负载测试时,我观察到线程一直处于打开状态(在线程转储中检查),几分钟后,它开始给出RejectedExecutionException

RejectedExecutionException
Task ServiceX rejected from org.springframework.scheduling.concurrent.
ThreadPoolTaskExecutor$1@4221a19e[Running, pool
size = 1000, active threads = 2, queued tasks = 10, completed tasks = 625216]
线程转储中显示的大多数线程

"executor-A-57" #579 prio=5 os_prio=0 tid=0x000000000193f800 nid=0x2e95 waiting on condition [0x00007fa9e820c000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x0000000582dadf90> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
    - None
Service1.java

package com.oyorooms.everything.threadpooling;

import org.springframework.scheduling.annotation.Async;

public class Service1 {
    public void set() throws InterruptedException {
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName());
    }
}

因此,理想情况下,对于我给出的线程池,应该只打开3个线程,但运行代码时仍然会遭到拒绝。

我建议通过添加一个输出任务执行器的记录器行来测试这一点,然后对不同的16个调用和45个请求进行计数。可能会有很多事情发生

  • 也许ThreadPoolTaskExecutor不是一个bean,而spring正在选择应用程序中配置的另一个bean
  • 也许应用程序的其他部分也在使用异步调用

  • 代码中可能存在一些永远循环的错误

  • 等等


    但是,如果没有单元测试,一个好的开始就是简单地记录正在发生的事情并分析日志。

    这是一个有趣的开始

    列出的代码失败的原因是,将元素从工作队列传输到工作线程所需的时间比主线程将项目放入队列所需的时间慢

    流程如下所示:

    if(there are active threads and is there availability on the queue){
        submit to the work queue for the worker threads to pick up // 1
    } else {
       if(max pool size is not met){
          create a new thread with this task being its first task // 2
       } else { 
          reject // 3
       }
    } 
    
    您看到的是代码命中了
    //3

    首次提交任务时,线程数将小于最大池大小。提交的第一轮任务将到达
    //2

    在第一次迭代之后,活动线程的数量将是最大池大小,代码将尝试提交到
    //1

    假设主线程非常快地将3个项目放入队列,因此线程池中的4个线程无法足够快地完成一个项目。如果发生这种情况,我们将传递第一个If语句(因为队列中没有可用性)并转到else。既然已经满足了最大池大小,那么除了
    拒绝
    之外就没有别的事情可做了

    这可以通过检查以下内容进一步解释

    如果请求无法排队,则会创建一个新线程,除非该线程将超过maximumPoolSize,在这种情况下,任务将被拒绝

    后来

    直接切换通常需要无限的MaximumPoolSize,以避免拒绝新提交的任务。这反过来又允许在命令继续以平均快于其处理速度的速度到达时出现无限线程增长的可能性

    要解决您的问题,您有两个合理的选择:

  • 使用一个。提供给SynchronousQueue的线程将无限期地等待,直到另一个线程接收该项目(如果它知道另一个线程正在等待接收该项目)。如果put未成功(即,另一个线程未立即将其关闭),您定义的固定队列大小将导致主线程返回(无阻塞)。要使用Spring使用SynchronousQueue,请将队列容量设置为零<代码>设置队列容量(0)
  • 。 同样来自Javadocs

    对于工作队列来说,一个很好的默认选择是SynchronousQueue,它将任务交给线程,而不占用线程

  • 将队列大小设置为大于或等于预期提交的并发任务数。队列的大小通常不会达到该大小,但它将在将来保护您


  • 如果没有任何代码,我无法验证,但您可能需要将队列大小增加到10以上。可能是工作线程无法足够快地拉动工作项的突发,因此达到了队列限制。是否确实正确配置了负载测试?45个并发请求等待所有45个请求完成吗?请注意,并行执行的最佳线程数是机器中(虚拟)处理器的数量。我怀疑它有1000个甚至400个。@JohnVint我使用Servlet过滤器和
    @WebFilter
    到专门用于api的线程池中,api的最大池大小为45个。@MarkA你的意思是任务首先进入队列,然后分配一个空闲线程给它吗?在问题本身中添加了类似的代码来复制问题。请看一看。
    if(there are active threads and is there availability on the queue){
        submit to the work queue for the worker threads to pick up // 1
    } else {
       if(max pool size is not met){
          create a new thread with this task being its first task // 2
       } else { 
          reject // 3
       }
    }