回到我们的“pool-1-thread-x”线程,这些线程都处在“Waiting”状态,从线程栈我们看到,这些线程“等待”在getTask方法调用上,线程尝试从线程池的队列中取任务,但是队列为空,所以通过LockSupport.park调用进到了“Waiting”状态。那“pool-1-thread-x”线程有多少个呢?通过下面这个命令来统计一下,结果是4096,正好跟线程池中的线程数相等。
grep -o 'pool-2-thread' 55790.log | wc -l
剩下CPU到底被谁消耗了?
应该怀疑CPU的上下文切换开销了,因为我们看到Java进程中的线程数比较多。
下面通过vmstat命令来查看一下操作系统层面的线程上下文切换活动:
cs那一栏表示线程上下文切换次数,in表示CPU中断次数,我们发现这两个数字非常高,基本证实了我们的猜测,线程上下文切切换消耗了大量CPU。
那具体是哪个进程导致的呢?
停止Spring Boot程序,再次运行vmstat命令,会看到in和cs都大幅下降,这就证实引起线程上下文切换开销的Java进程正是55790。
总结
遇到CPU过高,首先定位哪个进程导致的,之后可以通过top -H -p pid命令定位到具体的线程。
其次还要通jstack查看线程的状态,看看线程的个数或者线程的状态,如果线程数过多,可以怀疑是线程上下文切换的开销,我们可以通过vmstat和pidstat这两个工具进行确认。