问题发生
一天突然发现监控大屏cpu闲置率在80%左右,感觉不是很正常,现在需求不是很多,为什么cpu闲置率这么低,一般情况为95%以上,为了好奇开始了一次jvm线程的排查过程。
排查步骤
- 找出消耗cpu最高的pid ,第一种直接找到自己的项目名称,jps -l找到pid;第二种:top -c,如下图;
- 找出某pid下线程资源信息,执行top -H -p pid;然后将最高的tid转为16进制(printf “%x” 172)
-H 指显示线程,-p 是指定进程。通过该命令可以查看该进程里面的线程的cpu占用情况
- 执行jstack pid | grep 4ea2 -A10,在该线程堆栈中找到响应线程id,其中A10表示打印出当前10行数据;
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
注意:
有些人会报如下错误:
20130: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
有以下两个原因:1、不存在进程id为20130,所以需要你指定响应的进程id进行查找;2、当前登录用户无权限操作或者需要启动该进程的用户来执行,通过ps -aux命令查看所有进程的用户信息,然后切换用户su xxx ,执行jstack pid | grep 4ea2 -A10,亲测有效,不行你留言我帮你解决
结果
通过以上排查,我们发现线程名称为pool-41-thread -1 的线程不停的等待一些条件进行触发,然后根据栈信息我们发现我们系统中可能在某个地方使用线程池进行定时的操作,然后系统中全局查找发现确实在使用一个线程池一毫秒执行一次
因为建立线程池没有指定相应线程池工厂(ThreadFactory),所以使用了默认的线程池工厂,即Executors.defaultThreadFactory(),跟踪源码,终于找到罪魁祸首,如下:
优化方法
1.查到相应代码后,先分析代码为什么这样使用,然后进行适当优化
2.下一次新建线程池时,切记定义自己的线程池工厂,然后新建线程池指定自己的线程池工厂
代码如下:
/**
* 线程池工厂
*
* @author murongbin
* @date 2019/8/2 17:34
*/
public class NamedThreadFactory implements ThreadFactory {
/**
* 线程安全,保证每个线程是唯一的,具有原子性
*/
private static final AtomicInteger POOL_SEQ = new AtomicInteger(1);
private final AtomicInteger mThreadNum = new AtomicInteger(1);
/**
* 线程名前缀
*/
private final String mPrefix;
/**
* 是否为守护线程
*/
private final boolean daemonThread;
/**
* 线程组
*/
private final ThreadGroup mGroup;
public NamedThreadFactory() {
this("default-pool-" + POOL_SEQ.getAndIncrement(), false);
}
public NamedThreadFactory(String prefix) {
this(prefix, false);
}
public NamedThreadFactory(String prefix, boolean daemon) {
mPrefix = prefix + "-thread-";
daemonThread = daemon;
SecurityManager s = System.getSecurityManager();
mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
@Override
public Thread newThread(Runnable runnable) {
String name = mPrefix + mThreadNum.getAndIncrement();
Thread ret = new Thread(mGroup, runnable, name, 0);
ret.setDaemon(daemonThread);
return ret;
}
public ThreadGroup getThreadGroup() {
return mGroup;
}
}
使用如下:指定自定义的线程池工厂
private static final ExecutorService reloadExecutorService =
Executors.newCachedThreadPool(new NamedThreadFactory("ReloadConfigExecutorService",true));