在Java8中,什么可能导致等待状态线程一直增加

在Java8中,什么可能导致等待状态线程一直增加,java,multithreading,java-8,concurrency,Java,Multithreading,Java 8,Concurrency,今天我发现我的Java 8应用程序有很多线程处于等待状态: [arthas@1]$ thread --state RUNNABLE Threads Total: 3427, NEW: 0, RUNNABLE: 17, BLOCKED: 0, WAITING: 3114, TIMED_WAITING: 296, TERMINATED: 0 ID

今天我发现我的Java 8应用程序有很多线程处于等待状态:

[arthas@1]$ thread --state RUNNABLE
Threads Total: 3427, NEW: 0, RUNNABLE: 17, BLOCKED: 0, WAITING: 3114, TIMED_WAITING: 296, TERMINATED: 0                                                                              
ID             NAME                                         GROUP                          PRIORITY       STATE          %CPU           TIME           INTERRUPTED    DAEMON         
124            pool-11-thread-25                            main                           5              RUNNABLE       75             0:0            false          false          
53             as-command-execute-daemon                    system                         10             RUNNABLE       23             0:0            false          true           
133            Thread-20                                    main                           5              RUNNABLE       1              0:2            false          true           
28             Apollo-RemoteConfigLongPollService-1         Apollo                         5              RUNNABLE       0              0:0            false          true           
32             Attach Listener                              system                         9              RUNNABLE       0              0:0            false          true           
99             DestroyJavaVM                                main                           5              RUNNABLE       0              0:39           false          false          
4              Signal Dispatcher                            system                         9              RUNNABLE       0              0:0            false          true           
19             grpc-default-worker-ELG-1-1                  main                           5              RUNNABLE       0              0:0            false          true           
21             grpc-default-worker-ELG-1-2                  main                           5              RUNNABLE       0              0:0            false          true           
97             http-nio-11003-Acceptor                      main                           5              RUNNABLE       0              0:0            false          true           
85             http-nio-11003-BlockPoller                   main                           5              RUNNABLE       0              0:0            false          true           
96             http-nio-11003-ClientPoller                  main                           5              RUNNABLE       0              0:0            false          true           
54             lettuce-nioEventLoop-4-1                     main                           5              RUNNABLE       0              0:0            false          true           
70             lettuce-nioEventLoop-4-2                     main                           5              RUNNABLE       0              0:0            false          true           
36             nioEventLoopGroup-3-1                        system                         10             RUNNABLE       0              0:0            false          false          
42             nioEventLoopGroup-3-2                        system                         10             RUNNABLE       0              0:0            false          false          
37             nioEventLoopGroup-4-1                        system                         10             RUNNABLE       0              0:0            false          false          
Affect(row-cnt:0) cost in 120 ms.
有3000多个线程处于等待状态,现在我随机选择一个等待线程池线程,如下所示:

[arthas@1]$ thread 4410
"pool-96-thread-10" Id=4410 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@3e27c029
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@3e27c029
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    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)

Affect(row-cnt:0) cost in 16 ms.
但问题是,我不知道线程从何处开始,以及是什么导致等待线程增加。有没有办法找出从哪里开始线程或者为什么等待线程会增加?我现在正在使用Java线程执行器。目前,等待线程已达到6000+。我添加了一个自定义配置:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(30));
    }

}

您显示的堆栈跟踪是“正常情况”:这是一个线程池执行器线程,可以执行工作,但工作队列为空。在这种情况下,“等待”的意思是:我在等待一份工作,而不是:“我有事情要做,但不能做,因为我在等待事情先完成”

现在,3000个线程本身就有点令人担忧;每个线程都有自己的堆栈空间。它的大小取决于
-Xss
参数,但它们往往从64k变为1MB。如果是1MB,那就是3GB的堆栈空间,即。。。次优。这个数字(等待作业接受的线程数量)在VM升温后也不应该增长太多

如果所有/大多数等待线程都有类似的跟踪,那么实际上只有两个选项:

  • 您创建了一个执行器,并不断要求它添加越来越多的线程。我对此表示怀疑,但这是可能的
  • 你一直在做遗嘱执行人。不要这样做
遗嘱执行人的想法是你只做一个,或者至少很少做

如果您必须将它们创建为运行应用程序的一部分(与创建作业并将其提供给singleton executor的正常操作不同),那么请注意它们实际上是资源:如果您不“关闭”它们,您的进程将需要越来越多的资源,直到虚拟机在耗尽时崩溃

要关闭它们,您可以调用
shutdown()
,这是一个很好的请求;调用
shutdownNow()
,这是一个更具攻击性的命令,将所有尚未拾取的作业永久撤消

因此,总结一下:

  • 在应用程序的正常处理过程中,您正在创建新的执行器。在代码库中搜索
    新ScheduledThreadPoolExecutor
    ,并检查情况。如果必须查看此操作,请添加一些日志记录
  • 然后,最有可能的情况是,您首先要解决这个问题并停止创建新的执行器——只需创建一个,一次,然后将作业输入到这个执行器中
  • 如果真的有意义的话,使用一些守护者构造来确保你在使用完它们后也能清理它们。您可以搜索如何安全地执行此操作;这有点复杂,因为您需要决定如何处理队列中尚未完成的任何作业。如果这不是问题,很容易:
    .shutdown()
    将完成工作

您是否在应用程序的某个地方使用了配置了3000个线程的
ScheduledExecutorService
,而这些线程并不经常运行任务?我现在在项目中使用的是cron schedule@BEUNDEADID是否配置为能够适当地重用线程?您是否在创建时为它提供了一个自定义的
队列
,该队列不能容纳任何内容,因此每次都会创建一个新线程?看起来它每分钟都在持续增长。CRON是否每分钟都安排了任何可能出错并使线程处于不可用状态的内容?我猜您的代码一直在创建ScheduledThreadPoolExecutor的新实例,并且从未关闭它们。代码应该只创建一个实例,然后重新使用它。这就是线程池的目的,即重用池中的线程。简言之,您有一个资源泄漏,所以请检查代码并找到
ScheduledThreadPoolExecutor
对象的初始化位置。我在项目中没有找到任何
ScheduledThreadPoolExecutor
类,但我将调度任务配置为多线程。@Andreas